Spring Security 前后端分离
前后端分离指的就是前后端分离部署,前端 调用后端API,后端 返回 JSON格式数据,页面是由前端渲染并展示到浏览器中。Spring Security 涉及到的扩展点:登录成功处理:AuthenticationSuccessHandler登录失败处理:AuthenticationFailureHandler登出成功处理:LogoutSuccessHandler未登录处理:Authenticatio
前后端分离概述
前后端分离指的就是前后端分离部署,前端 调用后端API,后端 返回 JSON格式数据,页面是由前端渲染并展示到浏览器中。
相比较传统的单体项目 ,页面是由后端渲染完成后返回给浏览器的。(jsp、thymeleaf、html…)
Spring Security 现状
我们在 SpringSecurity入门篇,快速搭建一个安全Web服务 已经搭建了一个基本的示例。其中的关键的流程如下:
- 当未登录用户访问项目时,会重定向到登录页(
/login
)。 - 登录成功后,会重定向到首页(也就是
/
路径)。 - 登录失败的话,会重定向到登录页。
- 登出后,会重定向到登录页。
- 登录后,鉴权失败(访问权限不足)时,会返回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 {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
// 表单登录
.formLogin()
// 登录成功处理(对应流程2)
.successHandler(new AuthenticationSuccessHandler() {
@Override
public 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() {
@Override
public 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() {
@Override
public 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() {
@Override
public 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() {
@Override
public 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();
}
}
测试
直接测试原有流程,看看效果如何:
-
未登录时访问接口
很好,达到我们目的了,改造后,它并不是重定向到登录页,而是通过json响应回来401。ok -
登录失败
同样的以json格式响应。nice -
登录成功
输入正确的帐号密码,登录成功,json响应200。perfect -
登出
over over
另外
项目还是利用 cookie-session
机制维持会话状态。
统一响应格式利用code判断请求是否成功。(还有一种方式是利用 HTTP状态码)
源码
https://gitee.com/markix/spring-security-example/tree/master/spring-security-simple-restful
end
更多推荐
所有评论(0)