diff --git a/src/main/java/cn/xf/basedemo/common/model/LoginUser.java b/src/main/java/cn/xf/basedemo/common/model/LoginUser.java index 3db0d64..5a1a4a5 100644 --- a/src/main/java/cn/xf/basedemo/common/model/LoginUser.java +++ b/src/main/java/cn/xf/basedemo/common/model/LoginUser.java @@ -23,4 +23,14 @@ public class LoginUser implements Serializable { private String phone; private String token; + + /** + * 角色列表 + */ + private java.util.List roles; + + /** + * 权限列表 + */ + private java.util.List permissions; } diff --git a/src/main/java/cn/xf/basedemo/interceptor/SpringSecurityConfig.java b/src/main/java/cn/xf/basedemo/interceptor/SpringSecurityConfig.java index b10da62..b94b494 100644 --- a/src/main/java/cn/xf/basedemo/interceptor/SpringSecurityConfig.java +++ b/src/main/java/cn/xf/basedemo/interceptor/SpringSecurityConfig.java @@ -9,6 +9,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; @@ -37,6 +38,9 @@ public class SpringSecurityConfig { .requestMatchers("/doc.html", "/webjars/**", "/swagger-resources/**").permitAll() .anyRequest().authenticated() ) // 把自定义过滤器插入 Spring Security 过滤器链 + .sessionManagement(session -> session + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) .addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) .exceptionHandling(exceptions -> exceptions .authenticationEntryPoint((request, response, authException) -> { diff --git a/src/main/java/cn/xf/basedemo/interceptor/TokenAuthenticationFilter.java b/src/main/java/cn/xf/basedemo/interceptor/TokenAuthenticationFilter.java index a6ce42c..2e9ba75 100644 --- a/src/main/java/cn/xf/basedemo/interceptor/TokenAuthenticationFilter.java +++ b/src/main/java/cn/xf/basedemo/interceptor/TokenAuthenticationFilter.java @@ -7,8 +7,6 @@ import cn.xf.basedemo.common.model.LoginUser; import cn.xf.basedemo.common.model.RetObj; import cn.xf.basedemo.common.utils.ApplicationContextUtils; import cn.xf.basedemo.common.utils.RequestHeaderUtil; -import cn.xf.basedemo.mappers.SysPermissionMapper; -import cn.xf.basedemo.mappers.SysRoleMapper; import com.alibaba.fastjson.JSONObject; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -48,12 +46,6 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { @Autowired private RedisTemplate redisTemplate; - @Autowired - private SysPermissionMapper sysPermissionMapper; - - @Autowired - private SysRoleMapper sysRoleMapper; - @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //登录处理 @@ -89,29 +81,28 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { }finally { // 无论请求是否异常,最后一定清理,避免 ThreadLocal 泄漏 SessionContext.getInstance().clear(); + SecurityContextHolder.clearContext(); } } // Authentication auth = SecurityContextHolder.getContext().getAuthentication(); // CustomUserDetails user = (CustomUserDetails) auth.getPrincipal(); // Long userId = user.getUserId(); // 拿到登录用户 ID - /** - * 设置用户权限角色 (Spring Security 本身的 SecurityContext 是请求级别的,每次请求都会被清理,所以每次请求都会查询权限数据并设置, - * 安全但是很慢,所以可以做一些优化,比如把权限数据放到redis中获取和用户信息一起放在jwt中,然后登录时解析在设置到Spring security上下文中) - * @param loginUserInfo - */ private void setSpringSecurityContext(LoginUser loginUserInfo) { - //获取登录用户权限数据 - List permissionList = sysPermissionMapper.getPermissionListByRoleId(loginUserInfo.getId()); - //获取用户角色数据 - List roleList = sysRoleMapper.getRoleListByUserId(loginUserInfo.getId()); + //直接从缓存中获取登录用户权限和角色数据 + List permissionList = loginUserInfo.getPermissions(); + List roleList = loginUserInfo.getRoles(); + List authoritiesList = new java.util.ArrayList<>(); + if (!CollectionUtils.isEmpty(permissionList)) { + authoritiesList.addAll(permissionList); + } if (!CollectionUtils.isEmpty(roleList)) { //为角色拼接前缀 - roleList = roleList.stream().map(role -> "ROLE_" + role).collect(Collectors.toList()); + List roleAuthorities = roleList.stream().map(role -> "ROLE_" + role).collect(Collectors.toList()); + authoritiesList.addAll(roleAuthorities); } - permissionList.addAll(roleList); //封装用户权限角色 - List authorities = AuthorityUtils.createAuthorityList(permissionList); + List authorities = AuthorityUtils.createAuthorityList(authoritiesList); //设置用户信息到SpringSecurity上下文 UserDetails userDetails = new CustomUserDetails(loginUserInfo.getId(), loginUserInfo.getPhone(), authorities); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); diff --git a/src/main/java/cn/xf/basedemo/service/impl/UserServiceImpl.java b/src/main/java/cn/xf/basedemo/service/impl/UserServiceImpl.java index dc93c68..81fea93 100644 --- a/src/main/java/cn/xf/basedemo/service/impl/UserServiceImpl.java +++ b/src/main/java/cn/xf/basedemo/service/impl/UserServiceImpl.java @@ -6,6 +6,8 @@ import cn.xf.basedemo.common.model.RetObj; import cn.xf.basedemo.common.utils.JwtTokenUtils; import cn.xf.basedemo.common.utils.RSAUtils; import cn.xf.basedemo.config.GlobalConfig; +import cn.xf.basedemo.mappers.SysPermissionMapper; +import cn.xf.basedemo.mappers.SysRoleMapper; import cn.xf.basedemo.mappers.UserMapper; import cn.xf.basedemo.model.domain.User; import cn.xf.basedemo.model.res.LoginInfoRes; @@ -46,6 +48,12 @@ public class UserServiceImpl implements UserService { @Autowired private RedisTemplate redisTemplate; + @Autowired + private SysPermissionMapper sysPermissionMapper; + + @Autowired + private SysRoleMapper sysRoleMapper; + @Override public RetObj login(LoginInfoRes res) { @@ -84,6 +92,10 @@ public class UserServiceImpl implements UserService { String token = JwtTokenUtils.createToken(user.getId()); loginUser.setToken(token); + // 获取并缓存角色与权限数据到 Redis 避免后续过滤器的重复查库性能开销 + loginUser.setPermissions(sysPermissionMapper.getPermissionListByRoleId(user.getId())); + loginUser.setRoles(sysRoleMapper.getRoleListByUserId(user.getId())); + redisTemplate.opsForValue().set("token:" + token, JSONObject.toJSONString(loginUser), 3600, TimeUnit.SECONDS); redisTemplate.opsForValue().set("user_login_token:" + user.getId(), token, 3600, TimeUnit.SECONDS); return RetObj.success(loginUser);