目录
一、引入依赖及原理简述
二、多用户维护配置
三、基础自定义配置
四、前后端分离及登录结果管理  <— 你在这里 ( •̀ ω •́ )y
五、角色权限管理基础
从此处开始,为新的原创内容,相关数据结构代码换了一套新的,与之前的代码关系不大了。
建议新建一个项目,将配置文件复制过来,然后按照步骤走。
六、RBAC 结构实现
七、自定义响应式登录与 JWT 配置
八、集成 Redis
Spring Security(四)前后端分离及登录结果管理
博主前言:本以为这个就是代替传统 jwt 的插件,没想到复杂程度如此之高。Spring Security 本身是个高度自定义化的组件,必须花时间重点学习一下。以下为个人配置学习的流程,从零到权限管理、redis嵌入等步骤。
本文基于尚硅谷的 Spring Security 教程学习,文章与原教程有不小出入,仅供参考。
B站视频链接:尚硅谷Java项目SpringSecurity+OAuth2权限管理实战教程
现在的项目基本上都是前后端分离(包括我个人的一些开源项目),前后端分离的项目,后端验证后要向前端返回json数据,而不是跳转链接,所以要另行配置。
本文使用 fastjson 来处理序列化问题,读者亦可使用其他的方式处理,本文仅作参考。
一、定义并整合登录结果管理器
 这个东西实际上可以当作LoginController + LoginExceptionHandler等控制类。
 我通常不习惯手写返回体,习惯定义主返回体结构,再往里面放数据,这是我定义的通用返回结果类:
我使用的返回体为黑马的几个实战项目常用的返回体,并稍加改造。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 
 | 
 
 
 
 
 @Data
 public class Result<T> {
 
 
 
 private Integer code;
 
 
 
 private String msg;
 
 
 
 private T data;
 
 
 
 
 
 
 
 
 public static <T> Result<T> success(T data) {
 var result = new Result<T>();
 result.data = data;
 result.msg = "success";
 result.code = 200;
 return result;
 }
 
 
 
 
 public static <T> Result<T> success() {
 var result = new Result<T>();
 result.data = null;
 result.msg = "success";
 result.code = 200;
 return result;
 }
 
 
 
 
 
 
 public static <T> Result<T> error(int code, String msg) {
 var result = new Result<T>();
 result.code = code;
 result.msg = msg;
 return result;
 }
 }
 
 | 
 对于登录结果管理,我们要实现五件事:
- 登录成功之后,处理返回json数据
- 登录失败之后,处理返回json数据 * 2
- 注销成功之后,处理返回json数据 * 3
- 请求未认证后,处理返回json数据 * 4
- 会话并发处理,处理返回json数据 * 5
╮(╯▽╰)╭ 五次啊五次 ╮(╯▽╰)╭
会话并发处理:简单来讲就是后登录的账号会使先登录的账号失效
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 
 | 
 
 
 
 public class AuthenticationHandler implements AuthenticationSuccessHandler, AuthenticationFailureHandler, LogoutSuccessHandler, AuthenticationEntryPoint, SessionInformationExpiredStrategy {
 
 
 
 
 
 
 
 @Override
 public void onAuthenticationSuccess(
 HttpServletRequest request,
 HttpServletResponse response,
 Authentication authentication)
 throws IOException {
 
 var resultToken = "登录成功!这是登录后的 token 数据...";
 
 
 var result = Result.success(resultToken);
 var resultJSON = JSON.toJSONString(result);
 
 
 response.setContentType("application/json;charset=UTF-8");
 response.getWriter().println(resultJSON);
 }
 
 
 
 
 
 
 
 
 @Override
 public void onAuthenticationFailure(
 HttpServletRequest request,
 HttpServletResponse response,
 AuthenticationException exception)
 throws IOException {
 
 var result = Result.error(-1, exception.getLocalizedMessage());
 var resultJSON = JSON.toJSONString(result);
 
 
 response.setContentType("application/json;charset=UTF-8");
 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
 response.getWriter().println(resultJSON);
 }
 
 
 
 
 
 
 
 
 @Override
 public void onLogoutSuccess(
 HttpServletRequest request,
 HttpServletResponse response,
 Authentication authentication)
 throws IOException {
 
 var resultData = "注销成功!这是注销后的数据...";
 var result = Result.success(resultData);
 var resultJSON = JSON.toJSONString(result);
 
 
 response.setContentType("application/json;charset=UTF-8");
 response.getWriter().println(resultJSON);
 }
 
 
 
 
 
 
 
 
 @Override
 public void commence(
 HttpServletRequest request,
 HttpServletResponse response,
 AuthenticationException authException)
 throws IOException {
 
 var result = Result.error(-1, "登录已过期,请重新登录");
 var resultJSON = JSON.toJSONString(result);
 
 
 response.setContentType("application/json;charset=UTF-8");
 response.setStatus(HttpServletResponse.SC_FORBIDDEN);
 response.getWriter().println(resultJSON);
 }
 
 
 
 
 
 
 @Override
 public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException {
 
 var result = Result.error(-1, "该账号已从其他设备登录");
 var resultJSON = JSON.toJSONString(result);
 
 
 HttpServletResponse response = event.getResponse();
 response.setContentType("application/json;charset=UTF-8");
 response.setStatus(HttpServletResponse.SC_CONFLICT);
 response.getWriter().println(resultJSON);
 }
 }
 
 | 
 在这之后还没完,要在Config类中配置好,不然是不会生效的。
因为是前后端分离,不要忘了跨域问题
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | .cors(Customizer.withDefaults())
 
 .formLogin(form -> {
 
 form.loginPage("/login").permitAll()
 
 .successHandler(new AuthenticationHandler())
 
 .failureHandler(new AuthenticationHandler());
 })
 
 .logout(logout -> logout.logoutSuccessHandler(new AuthenticationHandler()))
 
 .exceptionHandling(exception -> exception.authenticationEntryPoint(new AuthenticationHandler()));
 
 .sessionManagement(session -> {
 
 session.maximumSessions(1)
 .expiredSessionStrategy(new AuthenticationHandler());
 });
 
 | 
二、用户认证信息分析
 上述登录结果管理器代码中,我们可以调用Authentication中的各项数据,其中的名称貌似跟用户没什么关系,它们实际上是 Spring Security 的一套新的概念。
 在 Spring Security 框架中,SecurityContextHolder、SecurityContext、Authentication、Principal和Credential是一些与身份验证和授权相关的重要概念。它们之间的关系如下:
- SecurityContextHolder:- SecurityContextHolder是 Spring Security 存储已认证用户详细信息的地方。
- SecurityContext:- SecurityContext是从- SecurityContextHolder获取的内容,包含当前已认证用户的- Authentication信息。
- Authentication:- Authentication表示用户的身份认证信息。它包含了用户的- Principal、- Credential和- Authority信息。
- Principal:表示用户的身份标识。它通常是一个表示用户的实体对象,例如用户名。- Principal可以通过- Authentication对象的- getPrincipal()方法获取。
- Credentials:表示用户的凭证信息,例如密码、证书或其他认证凭据。- Credential可以通过- Authentication对象的- getCredentials()方法获取。
- GrantedAuthority:表示用户被授予的权限