admin管理员组

文章数量:814285

Spring Security 前后端分离

前后端分离概述

前后端分离指的就是前后端分离部署前端 调用后端API后端 返回 JSON格式数据页面是由前端渲染并展示到浏览器中。

相比较传统的单体项目页面是由后端渲染完成后返回给浏览器的。(jsp、thymeleaf、html…)

Spring Security 现状

我们在 SpringSecurity入门篇,快速搭建一个安全Web服务 已经搭建了一个基本的示例。其中的关键的流程如下:

  1. 当未登录用户访问项目时,会重定向到登录页(/login)。
  2. 登录成功后,会重定向到首页(也就是 / 路径)。
  3. 登录失败的话,会重定向到登录页。
  4. 登出后,会重定向到登录页。
  5. 登录后,鉴权失败(访问权限不足)时,会返回403响应。

可以看出来,这妥妥的很不’前后端分离’,后端还会返回html页面并且各种重定向。好在 Spring Security 扩展性足够好,支持替换默认的实现,下文便来掰扯掰扯。

Spring Security 前后端分离改造

前后端分离的要点就是,后端的响应统一通过 JSON 格式返回,而不是html或者重定向。 基于这个出发点,我们将替换原有的实现类。

定义统一的响应格式

改造的第一点,则是定义一个统一的响应格式:

  • 统一的响应格式
@Data
@Accessors(chain = true)
public class RestResult<T> {private int code;private T data;private String msg;private RestResult() {}public static RestResult success() {return new RestResult().setCode(200);}public static RestResult success(Object data) {return success().setData(data);}public static RestResult error() {return new RestResult().setCode(500);}public static RestResult error(String errMsg) {return error().setMsg(errMsg);}public static RestResult error(int code, String errMsg) {return new RestResult().setCode(code).setMsg(errMsg);}public static RestResult error(Exception ex) {return error(ex.getMessage());}public String toJsonString() {return JsonUtils.toJsonString(this);}}

重写默认实现,并替换(重点)

涉及到的扩展点:

  • 登录成功处理:AuthenticationSuccessHandler
  • 登录失败处理:AuthenticationFailureHandler
  • 登出成功处理:LogoutSuccessHandler
  • 未登录处理:AuthenticationEntryPoint
  • 鉴权失败处理:AccessDeniedHandler

注意看代码中的注释,改造点对应上文中提到的原有流程的序号!

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated().and()// 表单登录.formLogin()// 登录成功处理(对应流程2).successHandler(new AuthenticationSuccessHandler() {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {response.setContentType("application/json;charset=utf-8");response.getWriter().write(RestResult.success().toJsonString());}})// 登录失败处理(对应流程3).failureHandler(new AuthenticationFailureHandler() {@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {response.setContentType("application/json;charset=utf-8");response.getWriter().write(RestResult.error(exception).toJsonString());}}).and()//登出.logout()//登出成功处理(对应流程4).logoutSuccessHandler(new LogoutSuccessHandler() {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {response.setContentType("application/json;charset=utf-8");response.getWriter().write(RestResult.success().toJsonString());}}).and()//异常处理.exceptionHandling()//未登录处理(对应流程1).authenticationEntryPoint(new AuthenticationEntryPoint() {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {response.setContentType("application/json;charset=utf-8");response.getWriter().write(RestResult.error(HttpServletResponse.SC_UNAUTHORIZED, "未登录").toJsonString());}})//没有权限处理(对应流程5).accessDeniedHandler(new AccessDeniedHandler() {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {response.setContentType("application/json;charset=utf-8");response.getWriter().write(RestResult.error(HttpServletResponse.SC_FORBIDDEN, "没有权限").toJsonString());}}).and().csrf().disable();}}

测试

直接测试原有流程,看看效果如何:

  1. 未登录时访问接口

    很好,达到我们目的了,改造后,它并不是重定向到登录页,而是通过json响应回来401。ok

  2. 登录失败

    同样的以json格式响应。nice

  3. 登录成功

    输入正确的帐号密码,登录成功,json响应200。perfect

  4. 登出

    over over

另外

项目还是利用 cookie-session 机制维持会话状态。
统一响应格式利用code判断请求是否成功。(还有一种方式是利用 HTTP状态码)

源码


end

本文标签: Spring Security 前后端分离