有时候页面跳转并不能满足我们,特别是在前后端分离开发中就不需要成功之后跳转页面。只需要给前端返回一个JSON通知登录成功还是失败与否。这个时候可以通过自定义AuthenticationSucccessHandler 实现。
public interface AuthenticationSuccessHandler {
default void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
this.onAuthenticationSuccess(request, response, authentication);
chain.doFilter(request, response);
}
void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException;
}
根据接口的描述信息,也可以得知登录成功会自动回调这个方法,进一步查看它的默认类实现,你会发现successForwardUrl、defaultSuccessUrl也是由它的子类实现的。
public class MyAuthenticationSuccessHandlerConfig implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Map result = new HashMap<>();
result.put("msg","登录成功!");
result.put("status",200);
result.put("authentication",authentication);
response.setContentType("application/json;charset=UTF-8");
String string = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(string);
}
}
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
...
.and()
.formLogin()
...
//认证成功时处理,前后端分离解决方案
.successHandler(new MyAuthenticationSuccessHandlerConfig())
.and()
//禁止 csrf 跨站请求保护
.csrf().disable();
}
}
经过源码debug分析可知,Security在登录失败之后会将异常信息存储到request 作用域或者session中 key为SPRING_SECURITY_LAST_EXCEPTION常量中。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
</head>
<body>
<h2>
<div th:text="${SPRING_SECURITY_LAST_EXCEPTION}"></div>
</h2>
...
</body>
</html>
和自定义登录成功一样,Spring Security 同样为前后端分离开发提供了登录失败的处理,这个类就是AuthenticationFailureHandler。源码为:
public interface AuthenticationFailureHandler {
/**
* Called when an authentication attempt fails.
* @param request the request during which the authentication attempt occurred.
* @param response the response.
* @param exception the exception which was thrown to reject the authentication
* request.
*/
void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException;
}
根据接口的描述信息,也可以得知登录失败会自动回调这个方法,进一步查看它的默认实例,会发现failureUrl、failureForwardUrl也是由它的子类实现的。
AuthenticationFailureHandler关系图
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
Map<String,Object> result = new HashMap<>();
result.put("msg","登录失败!"+exception.getMessage());
result.put("status",500);
response.setContentType("application/json;charset=UTF-8");
String string = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(string);
}
}
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
...
.anyRequest().authenticated()
.and()
.formLogin()
...
.failureHandler(new MyAuthenticationFailureHandler())
.and()
//禁止 csrf 跨站请求保护
.csrf().disable();
}
}
Spring Security中也提供了默认的注销登录配置,在开发时也可以按照自己需求对注销进行个性化定制。
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
...
.anyRequest().authenticated()
.and()
.formLogin()
...
.and()
.logout()
//指定注销登录的URl,默认的请求方式是GET
.logoutUrl("/logout")
//是否让当前session失效
.invalidateHttpSession(true)
//清除认证信息
.clearAuthentication(true)
//注销登录跳转页面
.logoutSuccessUrl("/login.html")
.and()
//禁止 csrf 跨站请求保护
.csrf().disable();
}
}
配置多个注销登录请求
如果项目中有需要,开发者还可以配置多个注销登录的请求,同时还可以指定请求的方法:
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
...
.anyRequest().authenticated()
.and()
.formLogin()
...
.and()
.logout()
.logoutRequestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/aa","GET"),
new AntPathRequestMatcher("/bb","POST")
))
...
.and()
//禁止 csrf 跨站请求保护
.csrf().disable();
}
}