文章目录
回顾
之前在《循序渐进学习 spring security 第三篇,如何自定义登录页面?登录回调?》中介绍了如何通过 spring security 自定义登录页面和登录成功失败的回调的配置,可以根据配置指定的 URL 跳转页面,要么重定向,要么服务端跳转,如果对于 spring security 自定义登录页面不熟悉的可以回去看看我之前的文章。但是在很多企业项目中,大多数都是前后端分离的,这种场景下,如果通过配置跳转页面,就会显得不够灵活。那么,如何才能更灵活实现前后端分离,前端能自由跳转指定的页面呢?这就是今天要讨论的内容。
前后端分离的数据交互神器–JSON
在前后端分离情况下前后端的交互都是通过 JSON 来进行,无论登录成功还是失败,都不会有什么服务端跳转或者客户端跳转之类。
登录成功了,服务端就返回一段登录成功的提示 JSON 给前端,前端收到之后由前端自己决定干嘛干嘛,就和后端没有关系了。
登录失败了,服务端就返回一段登录失败的提示 JSON 给前端,前端收到之后,由前端自己决定干嘛,也和后端没有关系了。
未认证,服务端就返回一段接口未认证的提示 JSON 给前端,前端收到之后,由前端自己决定干嘛,也和后端没有关系了。
注销登录,服务端就返回一段接口未认证的提示 JSON 给前端,前端收到之后,由前端自己决定干嘛,也和后端没有关系了。
首先把这样的思路确定了,基于这样的思路,我们来看一下登录配置
前面我们介绍了登录成功重定向 URL 相关的方法有两个:
defaultSuccessUrl
successForwardUrl
这两个都是配置跳转地址的,适用于前后端不分的开发。除了这两个方法之外,还有一个 successHandler。其实 successHandler 的功能已经囊括了 defaultSuccessUrl 和 successForwardUrl 的功能
与登录成功相似,登录失败也有类似的回调 failureHandler
登录成功 successHandler 功能介绍
.successHandler((req,resp,authentication)->{
Object principal = authentication.getPrincipal();
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(JSON.toJSONString(principal));
out.flush();
out.close();
})
successHandler 方法的参数是一个 AuthenticationSuccessHandler 对象,这个对象中我们要实现的方法是 onAuthenticationSuccess。
onAuthenticationSuccess 方法有三个参数,分别是:
HttpServletRequest
HttpServletResponse
Authentication
-
有了前两个参数,我们就可以在这里随心所欲的返回数据了。利用 HttpServletRequest 我们可以做服务端跳转,利用 HttpServletResponse 我们可以做客户端跳转,当然,也可以返回 JSON 数据。
-
第三个 Authentication 参数则保存了我们刚刚登录成功的用户信息。
配置完成后,我们再去登录,就可以看到登录成功的用户信息通过 JSON 返回到前端了,如下:
当然用户的密码已经被擦除掉了。擦除密码的问题,在上一篇《循序渐进雪 spring security 第四篇,登录流程是怎样的?登录用户信息保存在哪里?》已经有介绍,这样做的目的,也是为了确保用户密码安全
登录失败 failureHandler 功能介绍
.failureHandler((req, resp, e) -> {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(JSON.toJSONString(e));
out.flush();
out.close();
})
失败的回调也是三个参数
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception
前两个就不用说了,第三个是一个 Exception,对于登录失败,会有不同的原因,Exception 中则保存了登录失败的原因,我们可以将之通过 JSON 返回到前端。
针对这个异常,可以根据异常分类,当然,如果有这个必要的话,可以分好类,返回比较明确的异常信息给到前端
未认证处理方案
系统默认的行为是直接重定向到登录页面,在前后端分离中,这个逻辑明显是有问题的,要解决这个问题,就涉及到 Spring Security 中的一个接口 AuthenticationEntryPoint ,该接口有一个实现类:LoginUrlAuthenticationEntryPoint ,该类中有一个方法 commence,这个方法是用来决定到底是要重定向还是要 forward 的
那么我们解决问题的思路很简单,直接重写这个方法,在方法中返回 JSON 就可以了
.exceptionHandling()
.authenticationEntryPoint((req, resp, authException) -> {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(JSON.toJSONString("尚未登录,请先登录"));
out.flush();
out.close();
}
)
该方法中直接返回相应的 JSON 提示即可。如果用户再去直接访问一个需要认证之后才可以访问的请求,就不会发生重定向操作了,服务端会直接给浏览器一个 JSON 提示,浏览器收到 JSON 之后,自己决定要去干嘛干嘛。
注销登录
注销登录我们前面说过,按照前面的配置,注销登录之后,系统自动跳转到登录页面,这也是不合适的,在产品开发中基本上都不合理,那么我们注销登录成功后返回 JSON 即可
.logout()
.logoutSuccessHandler((req, resp, authentication) -> {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(JSON.toJSONString("注销成功"));
out.flush();
out.close();
})
OK,本文主要介绍几种常见的 JSON 交互的场景
源码下载 项目 security-json 就是,通过注释和开放未认证处理接口去感受 demo