述
上文中,我们实现了一个自定义的可配置的登录页,效果就是首先一个请求过来,如果需要认证的话,就跳转到认证的界面,然后认证完成之后,再跳转到最开始访问的页面
在实际的环境中,我们登录成功之后可能并不是去跳转页面, 在前后端分离的环境中一般都是返回一个json格式的用户信息,然后登录失败也是返回具体的原因给前端
下面就来看一下如何实现自定义的登录成功/失败后返回json数据
自定义登录成功处理
登录成功处理只需要去实现 MyAuthenticationSuccessHandler
接口, 这个接口很简单,里面就一个方法,看一下源码
这里的参数有一个 Authentication
对象,我们之前看认证处理源码的时候,可以看到认证完成之后就会返回这个对象,然后在我们自定义的实现里面,就把这个对象转成json返回去
新建一个类 MyAuthenticationSuccessHandler
实现这个接口,重写里面的方法,具体代码如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17@Slf4j
@Component("myAuthenticationSuccessHandler")
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
log.info("用户登录成功...");
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(authentication));
}
}
这里就是直接把 Authentication
这个对象转成json字符串然后返回去了,然后这个类加了 @Component("myAuthenticationSuccessHandler")
这个注解,交给spring去管理,然后起了个名字,下面的配置要用到
事件写好之后还需要把这个handler加到 security 的配置中去,才能让他生效,修改 BrowserSecurityConfig
部分代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13@Autowired
private AuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/authentication/require")
.loginProcessingUrl("/authentication/from")
.successHandler(myAuthenticationSuccessHandler)
.and()
// ... 省略其他代码
}
先把我们刚刚创建的 MyAuthenticationSuccessHandler
注入进去,然后通过 successHandler()
方法去设置
测试
配置完成之后,启动项目,随便访问一个接口,然后登录,返回值如下:
可以看到返回的就是一串json字符串,格式化一下,如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19{
"authorities": [{"authority": "admin"}],
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": "CAD61C0C6DB0DE0C5FDF4893061C1247"
},
"authenticated": true,
"principal": {
"password": null,
"username": "zhangsan",
"authorities": [{"authority": "admin"}],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"credentials": null,
"name": "zhangsan"
}
这里就包括了用户的权限,用户名,以及我们认证返回的那个 UserDetail 的信息, 这个基本都能看得懂 就不做过多解释了
自定义登录失败处理
登录失败和成功是类似的,只不过实现的接口不一样, 这里要实现的是 AuthenticationFailureHandler
这个接口,看一下源码
里面也是只有一个方法,唯一的区别是这里的参数是 AuthenticationException
,这个就是认证失败的异常,然后我们在自定义的实现里面,把这个对象也转成json返回给前端
新建类 MyAuthenticationFailureHandler
,代码如下:
1 | @Slf4j |
然后,还是在 BrowserSecurityConfig
中配置一下,修改代码如下:
1 | @Autowired |
和上面成功的配置基本是一样的
测试
完成之后,做一下测试,启动项目,随便访问一个接口,然后输入一个错误的密码,返回如下:
返回值简化一下,如下:1
2
3
4
5
6
7{
"cause": null,
"stackTrace":[]
"localizedMessage": "坏的凭证",
"message": "坏的凭证",
"suppressed": []
}
stackTrace
里面是一些java的错误堆栈信息,这个不看,然后其他的就是登陆失败的原因返回去了
到这儿,自定义的登录成功/失败的处理都完成了
升级改造
通过上面的配置,我们在任何时候都是返回的json数据给前端, 那可能实际使用中就是想直接跳转请求,而不是返回json数据
我们这里可以做成一个可配置的,就跟之前的登录页面一样,由调用服务的一方去决定是返回json还是去跳转页面, 下面看一下如何实现
配置类改造
还是和登录页一样,在上文中的浏览器配置类里面加一个配置,如下:1
2
3
4/**
* 登录返回类型
*/
private LoginResponseType loginType = LoginResponseType.JSON;
这里用到了一个我们自己定义的枚举, LoginResponseType
, 代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public enum LoginResponseType implements Serializable {
/**
* 跳转请求
*/
REDIRECT,
/**
* json返回
*/
JSON,
;
}
里面之后两个,一个跳转一个json返回,然后配置类中给的默认的是返回json的
handler改造
配置类搞好之后,再来改造之前的两个handler, 先看登录成功的这个 MyAuthenticationFailureHandler
, 我们之前实现的 AuthenticationFailureHandler
. 现在 改成继承 SavedRequestAwareAuthenticationSuccessHandler
这个是 Spring Security 默认的成功处理 ,然后我们重写里面的方法,具体代码如下:
1 | @Slf4j |
这里就是加了个判断,如果配置是json的话,就返回json,如果不是,那就调用父类的处理方式,就是 Spring Security 默认的成功处理
再看下失败的处理, 在 MyAuthenticationFailureHandler
中我们是实现的 AuthenticationFailureHandler
这个接口,然后现在改成继承 SimpleUrlAuthenticationFailureHandler
, 然后里面方法和上面一样,也是加个判断就ok了, 代码如下:
1 | @Slf4j |
同样的,如果配置的不是返回json就调用父类的处理方式
上面的内容处理完成之后,就可以在demo项目中的 application.yml
中去配置了,如下:1
2
3
4core:
security:
browser:
loginResponseType: REDIRECT
测试
配置都完成之后,来测试看下效果,application.yml
中,先不做任何配置, 直接用默认的就是json的,然后启动项目,效果如下:
然后是认证失败的,如下:
最后把配置加上,重启,改成Security 默认的处理方式,来看下效果,如下:
总结
本文主要实现了自定义的登录成功/失败处理
分别通过实现 AuthenticationSuccessHandler
和 AuthenticationFailureHandler
这两个接口实现
Spring Security 默认的实现是 SavedRequestAwareAuthenticationSuccessHandler
和 SimpleUrlAuthenticationFailureHandler
可以通过继承这两个类来重写默认的处理方式
代码已经上传github,传送门