第三章已经可以从数据库查出密码进行各种校验了,但是对于各种登录认证流程来说还是单调,无法满足我们的企业级开发需求,本篇就进行一个个性化用户认证流程处理,包括自定义登录页面,自定义登录成功处理,自定义登陆失败处理。
自定义登录页面
自定义的登录页:meicloud-signIn.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h2>标准登录页面</h2>
<h3>表单登录</h3>
<form action="/authentication/form" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2"><button type="submit">登录</button></td>
</tr>
</table>
</form>
</body>
</html>
配置自定义登录页
- 在我们前面篇章讲到的 BrowserSecurityConfig 配置类中进行配置,如下
.loginPage()
配置项
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
// 使用自定义的表单登录页面
.loginPage("/meicloud-signIn.html")
// 以下这行 UsernamePasswordAuthenticationFilter 会知道要处理表单的 /authentication/form 请求,而不是默认的 /login
.loginProcessingUrl("/authentication/form")
.and()
.authorizeRequests()
// 排除对 "/meicloud-signIn.html" 的身份验证
.antMatchers("/meicloud-signIn.html").permitAll()
// 表示所有请求都需要身份验证
.anyRequest()
.authenticated()
.and()
.csrf().disable();// 暂时把跨站请求伪造的功能关闭掉
}
.loginProcessingUr()
表示要处理表单的/authentication/form
登录请求,而不是默认的/login
- 要注意的是,
.anyRequest
表示所有请求都需要身份验证,导致我们的登录页/meicloud-signIn.html
也需要进行身份验证,所以我们需要配置.antMatchers("/meicloud-signIn.html").permitAll()
来使得登录页不需要进行拦截认证。
自定义登录成功处理
实现AuthenticationSuccessHandler
接口,实现onAuthenticationSuccess()
方法
- 但是一般情况下不会去直接实现该接口,一般可以继承该接口的实现类,如下,这样使得给到前端的请求有更多种选择,比如JSON或者是跳到另一个路径。
@Component("meicloudAuthenticationSuccessHandler")
public class MeicloudAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private ObjectMapper objectMapper;
@Autowired
private SecurityProperties securityProperties;
private RequestCache requestCache = new HttpSessionRequestCache();
/**
* 登录成功处理
* @param authentication 封装认证信息,包括认证请求信息,ip、session信息等,还包括认证通过以后,自定义的UserDetailService返回的UserDetails信息
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
logger.info("登录成功");
if (LoginResponseType.JSON.equals(securityProperties.getBrowser().getSignInResponseType())) {
response.setContentType("application/json;charset=UTF-8");
String type = authentication.getClass().getSimpleName();
response.getWriter().write(objectMapper.writeValueAsString(new SimpleResponse(type)));
} else {
// 如果设置了meicloud.security.browser.singInSuccessUrl,总是跳到设置的地址上
// 如果没设置,则尝试跳转到登录之前访问的地址上,如果登录前访问地址为空,则跳到网站根路径上
if (StringUtils.isNotBlank(securityProperties.getBrowser().getSingInSuccessUrl())) {
requestCache.removeRequest(request, response);
setAlwaysUseDefaultTargetUrl(true);
setDefaultTargetUrl(securityProperties.getBrowser().getSingInSuccessUrl());
}
super.onAuthenticationSuccess(request, response, authentication);
}
}
}
- 然后需要在
BrowserSecurityConfig
配置类中进行配置,加上一行.successHandler()
配置
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/meicloud-signIn.html")
.loginProcessingUrl("/authentication/form")
// 配置成功处理
.successHandler(meicloudAuthenticationSuccessHandler)
.and()
.authorizeRequests()
.antMatchers("/meicloud-signIn.html").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().disable();
}
自定义登陆失败处理
和登录成功处理类似,实现 AuthenticationFailHandler 接口,实现onAuthenticationFailure()
方法
- 同理一样不会去直接实现该接口,可以继承它的实现类
SimpleUrlAuthenticationFailureHandler
@Component("meicloudAuthenctiationFailureHandler")
class MeicloudAuthencationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private ObjectMapper objectMapper;
@Autowired
private SecurityProperties securityProperties;
/**
* 登录失败处理
* @param exception 包含了登录错误时的异常,包括用户名没找着、密码不正确等等
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
logger.info("登录失败");
if (LoginResponseType.JSON.equals(securityProperties.getBrowser().getSignInResponseType())) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(new SimpleResponse(exception.getMessage())));
}else{
super.onAuthenticationFailure(request, response, exception);
}
}
}
- 同样需要在
BrowserSecurityConfig
中添加.failureHandler()
配置
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/meicloud-signIn.html")
.loginProcessingUrl("/authentication/form")
// 配置成功处理
.successHandler(meicloudAuthenticationSuccessHandler)
.failureHandler(meicloudAuthenticationFailureHandler)
.and()
.authorizeRequests()
.antMatchers("/meicloud-signIn.html").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().disable();
}
总结
需求 | 实现 |
---|---|
自定义登录页面 | http.formLogin().loginPage("/meicloud-signIn.html") |
自定义登录成功处理 | AuthenticationSuccessHandler |
自定义登录失败处理 | AuthenticationFailureHandler |