文章目录
- 介绍
- 实现功能
- 系统环境配置
- 开发步骤
- 引入jar包
- 定义登录和授权页面
- 定义一个关于登录和授权的控制器
- 配置页面授权
- 测试
- 项目源码
介绍
在上一章节中,。我们是实现了简单的oauth2服务提供商。但还不能满足现实需要,在现实中,我们需要定制特质的登录和授权页面。下面将讲解如何自定义。
实现功能
能自定义登录和授权页面。
系统环境配置
- JDK1.8+
- mysql5.6+
- Redis
开发步骤
引入jar包
首先引入的是thymeleaf。用于html页面开发
<!--页面要用到的框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
其他包读者可查阅源码了解。
定义登录和授权页面
1.登录页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>登录</title>
</head>
<body>
<h2>标准登录页面</h2>
<h3>表单登录</h3>
<!--spring security 默认处理用户名密码就是/login,可以自定义,需要loginProcessingUrl()-->
<p style="color: red" th:if="${param.error}">用户名或密码错误</p>
<form action="/authentication/form" method="post">
<table>
<tr>
<td>用户名:</td>
<td>
<label><input type="text" name="username"/></label>
</td>
</tr>
<tr>
<td>密码:</td>
<td>
<label><input type="password" name="password"/></label>
</td>
</tr>
<tr>
<td colspan="2">
<button type="submit">登录</button>
</td>
</tr>
</table>
</form>
</body>
</html>
- 授权页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>确认授权页面</title>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no"/>
<link rel="stylesheet" href="//i.gtimg.cn/vipstyle/frozenui/2.0.0/css/frozen.css"/>
<style>
.block{
position: relative;
}
.ui-notice{
position: relative;
padding:20px 15px;
box-sizing: border-box;
}
.ui-notice p{
color:#333;
font-weight: 600;
}
.ui-btn-primary{
background-color: #02cd93;
border-color:#02cd93;;
}
.ui-notice-btn{
padding:50px 0px 15px;
}
</style>
</head>
<body>
<div class="block">
<section class="ui-notice">
<i class="icon icon-notice"></i>
<p>是否授权:<span th:text="${session.authorizationRequest.clientId}">clientId</span></p>
<div class="ui-notice-btn">
<form id='confirmationForm' name='confirmationForm' action="/oauth/authorize" method='post'>
<input name='user_oauth_approval' value='true' type='hidden'/>
<!--写好授权访问领域-->
<input th:name="${s}" value="true" type="hidden" th:each="s : ${scopeList}"/>
<input class="ui-btn-primary ui-btn-lg ui-btn-primary" name='authorize' value='授权' type='submit'/>
</form>
</div>
</section>
</div>
</body>
</html>
作者只是简单定义页面而已。读者可以根据需求自己定义。
定义一个关于登录和授权的控制器
在这个类中,需要处理登录请求,页面的跳转,以及授权页面。
/**
* @author lvhaibao
* @description 处理登录和授权的控制器
* @date 2018/12/26 0026 17:31
*/
@Slf4j
@Controller
@SessionAttributes({"authorizationRequest"})
public class AuthPageController {
private RequestCache requestCache = new HttpSessionRequestCache();
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Autowired
private SecurityProperties securityProperties;
/**
* 当用户没登录的时候,会经过这个请求,在这个请求中可以处理一些逻辑
*
* @param request request
* @param response response
* @return ResultModel
* @throws IOException IOException
*/
@RequestMapping(FromLoginConstant.LOGIN_PAGE)
@ResponseBody
@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
public ResultModel requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (null != savedRequest) {
String targetUrl = savedRequest.getRedirectUrl();
log.info("引发跳转的请求是:" + targetUrl);
redirectStrategy.sendRedirect(request, response, FromLoginConstant.AFTER_LOGING_PAGE);
}
//如果访问的是接口资源
return ResultModel.fail(401, "访问的服务需要身份认证,请引导用户到登录页");
}
@RequestMapping(FromLoginConstant.AFTER_LOGING_PAGE)
public String login() {
return securityProperties.getOauthLogin().getOauthLogin();
}
/**
* 自定义授权页面,注意:一定要在类上加@SessionAttributes({"authorizationRequest"})
*
* @param model model
* @param request request
* @return String
* @throws Exception Exception
*/
@RequestMapping("/oauth/confirm_access")
public String getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {
@SuppressWarnings("unchecked")
Map<String, String> scopes = (Map<String, String>) (model.containsKey("scopes") ? model.get("scopes") : request.getAttribute("scopes"));
List<String> scopeList = new ArrayList<>();
if (scopes != null) {
scopeList.addAll(scopes.keySet());
}
model.put("scopeList", scopeList);
return securityProperties.getOauthLogin().getOauthGrant();
}
在这个类中,定义了一些常量或者配置。读者可以很根据源码找到相关的配置信息的类。
配置页面授权
在上面定义的请求中,需要对这类请求进行处理。
/**
* @author lvhaibao
* @description 浏览器配置
* @date 2018/12/25 0025 10:53
*/
@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SecurityProperties securityProperties;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//表单登录,loginPage为登录请求的url,loginProcessingUrl为表单登录处理的URL
.formLogin().loginPage(FromLoginConstant.LOGIN_PAGE).loginProcessingUrl(FromLoginConstant.LOGIN_PROCESSING_URL)
//允许访问
.and().authorizeRequests().antMatchers(
"/user/hello",
FromLoginConstant.LOGIN_PROCESSING_URL,
FromLoginConstant.LOGIN_PAGE,
securityProperties.getOauthLogin().getOauthLogin(),
securityProperties.getOauthLogin().getOauthGrant()).permitAll().anyRequest().authenticated()
//禁用跨站伪造
.and().csrf().disable();
}
}
测试
- 测试过程和上一章节一样。只是登录页面和授权页面改变了。这里就不进行测试
项目源码
https://gitee.com/lvhaibao/spring-lhbauth/tree/9a99e050b685df563825493bff5a4694e1f12eaf/