feat: 增加了 jwt 验证方式。
This commit is contained in:
@@ -70,6 +70,10 @@ dependencies {
|
|||||||
// Excel处理
|
// Excel处理
|
||||||
implementation("org.apache.poi:poi:5.2.5")
|
implementation("org.apache.poi:poi:5.2.5")
|
||||||
implementation("org.apache.poi:poi-ooxml:5.2.5")
|
implementation("org.apache.poi:poi-ooxml:5.2.5")
|
||||||
|
|
||||||
|
// Redis
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-data-redis")
|
||||||
|
implementation("org.apache.commons:commons-pool2")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<Test> {
|
tasks.withType<Test> {
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ public class UrlPermissionAuthorizationManager implements AuthorizationManager<R
|
|||||||
|
|
||||||
HttpServletRequest request = context.getRequest();
|
HttpServletRequest request = context.getRequest();
|
||||||
String requestURI = request.getRequestURI();
|
String requestURI = request.getRequestURI();
|
||||||
if (antPathMatcher.match("/auth/user/me", requestURI)) {
|
// 公开接口直接放行
|
||||||
|
if (antPathMatcher.match("/auth/user/me", requestURI)
|
||||||
|
|| antPathMatcher.match("/open/**", requestURI)) {
|
||||||
return new AuthorizationDecision(true);
|
return new AuthorizationDecision(true);
|
||||||
}
|
}
|
||||||
Authentication auth = authentication.get();
|
Authentication auth = authentication.get();
|
||||||
|
|||||||
@@ -4,47 +4,66 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import com.niuan.erp.common.base.BaseResult;
|
import com.niuan.erp.common.base.BaseResult;
|
||||||
import com.niuan.erp.common.base.LoginUser;
|
import com.niuan.erp.common.base.LoginUser;
|
||||||
import com.niuan.erp.common.bean.UrlPermissionAuthorizationManager;
|
import com.niuan.erp.common.bean.UrlPermissionAuthorizationManager;
|
||||||
|
import com.niuan.erp.common.security.JwtAuthenticationFilter;
|
||||||
|
import com.niuan.erp.common.security.TokenService;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.core.session.SessionRegistry;
|
import org.springframework.security.core.session.SessionRegistry;
|
||||||
import org.springframework.security.core.session.SessionRegistryImpl;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
import org.springframework.web.cors.CorsConfigurationSource;
|
import org.springframework.web.cors.CorsConfigurationSource;
|
||||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security配置类 - 同时支持Session和JWT两种认证方式
|
||||||
|
* - Web端(后台管理):使用传统Session方式(基于Cookie)
|
||||||
|
* - 小程序端:使用JWT方式(基于Token)
|
||||||
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity // 启用 Web 安全
|
@EnableWebSecurity
|
||||||
@EnableMethodSecurity
|
@EnableMethodSecurity
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
private final UrlPermissionAuthorizationManager urlPermissionAuthorizationManager;
|
private final UrlPermissionAuthorizationManager urlPermissionAuthorizationManager;
|
||||||
|
|
||||||
|
private final JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||||
|
|
||||||
|
private final TokenService tokenService;
|
||||||
|
|
||||||
|
private final UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http, SessionRegistry sessionRegistry) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http, SessionRegistry sessionRegistry) throws Exception {
|
||||||
http
|
http
|
||||||
// 👇 1. 启用 CSRF
|
// 禁用 CSRF(JWT需要禁用,Session方式通过其他方式防护)
|
||||||
.csrf(csrf -> csrf.disable())
|
.csrf(csrf -> csrf.disable())
|
||||||
|
|
||||||
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
|
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
|
||||||
|
|
||||||
// 👇 2. 设置 Session
|
// 设置 Session(支持Session方式,同时允许JWT无状态请求)
|
||||||
.sessionManagement(session -> session
|
.sessionManagement(session -> session
|
||||||
.maximumSessions(1)
|
.maximumSessions(1)
|
||||||
.maxSessionsPreventsLogin(false)
|
.maxSessionsPreventsLogin(false)
|
||||||
.sessionRegistry(sessionRegistry))
|
.sessionRegistry(sessionRegistry))
|
||||||
|
|
||||||
// 👇 3. 请求授权规则
|
// 请求授权规则
|
||||||
.authorizeHttpRequests(authz -> authz
|
.authorizeHttpRequests(authz -> authz
|
||||||
.requestMatchers(
|
.requestMatchers(
|
||||||
"/v3/api-docs/**", "/v3/api-docs",
|
"/v3/api-docs/**", "/v3/api-docs",
|
||||||
@@ -52,24 +71,25 @@ public class SecurityConfig {
|
|||||||
"/swagger-ui.html",
|
"/swagger-ui.html",
|
||||||
"/webjars/**",
|
"/webjars/**",
|
||||||
"/doc.html",
|
"/doc.html",
|
||||||
"/favicon.ico").permitAll()
|
"/favicon.ico",
|
||||||
|
"/open/**",
|
||||||
|
"/api/auth/**").permitAll() // 小程序登录接口公开
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
)
|
)
|
||||||
|
|
||||||
//
|
// 表单登录配置(Web端使用)
|
||||||
.formLogin(form -> form
|
.formLogin(form -> form
|
||||||
.loginProcessingUrl("/auth/login") // 指定登录提交地址
|
.loginProcessingUrl("/auth/login")
|
||||||
.successHandler((request, response, authentication) -> {
|
.successHandler((request, response, authentication) -> {
|
||||||
// 登录成功:返回 JSON
|
// Web端登录成功:返回简单JSON
|
||||||
response.setContentType("application/json;charset=UTF-8");
|
response.setContentType("application/json;charset=UTF-8");
|
||||||
LoginUser user = (LoginUser) authentication.getPrincipal();
|
|
||||||
response.getWriter().write(new ObjectMapper().writeValueAsString(BaseResult.success()));
|
response.getWriter().write(new ObjectMapper().writeValueAsString(BaseResult.success()));
|
||||||
})
|
})
|
||||||
.failureHandler((request, response, exception) -> {
|
.failureHandler((request, response, exception) -> {
|
||||||
// 登录失败:返回 JSON
|
|
||||||
response.setStatus(HttpStatus.OK.value());
|
response.setStatus(HttpStatus.OK.value());
|
||||||
response.setContentType("application/json;charset=UTF-8");
|
response.setContentType("application/json;charset=UTF-8");
|
||||||
response.getWriter().write(new ObjectMapper().writeValueAsString(BaseResult.error(3, exception.getMessage())));
|
response.getWriter().write(new ObjectMapper().writeValueAsString(
|
||||||
|
BaseResult.error(3, exception.getMessage())));
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.logout(logout -> logout
|
.logout(logout -> logout
|
||||||
@@ -78,47 +98,50 @@ public class SecurityConfig {
|
|||||||
.deleteCookies("JSESSIONID")
|
.deleteCookies("JSESSIONID")
|
||||||
.clearAuthentication(true)
|
.clearAuthentication(true)
|
||||||
.logoutSuccessHandler((request, response, authentication) -> {
|
.logoutSuccessHandler((request, response, authentication) -> {
|
||||||
|
// Web端登出
|
||||||
response.setContentType("application/json;charset=UTF-8");
|
response.setContentType("application/json;charset=UTF-8");
|
||||||
response.getWriter().write(new ObjectMapper().writeValueAsString(BaseResult.success()));
|
response.getWriter().write(new ObjectMapper().writeValueAsString(BaseResult.success()));
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
// 👇 6. 异常处理(认证失败 / 权限不足)
|
// 异常处理
|
||||||
.exceptionHandling(ex -> ex
|
.exceptionHandling(ex -> ex
|
||||||
.authenticationEntryPoint((request, response, authException) -> {
|
.authenticationEntryPoint((request, response, authException) -> {
|
||||||
response.setStatus(401);
|
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||||
response.getWriter().write("Unauthorized: " + authException.getMessage());
|
response.setContentType("application/json;charset=UTF-8");
|
||||||
|
response.getWriter().write(new ObjectMapper().writeValueAsString(
|
||||||
|
BaseResult.error(HttpStatus.UNAUTHORIZED.value(), "未认证,请先登录")));
|
||||||
})
|
})
|
||||||
.accessDeniedHandler((request, response, accessDeniedException) -> {
|
.accessDeniedHandler((request, response, accessDeniedException) -> {
|
||||||
response.setStatus(403);
|
response.setStatus(HttpStatus.FORBIDDEN.value());
|
||||||
response.getWriter().write("Forbidden: " + accessDeniedException.getMessage());
|
response.setContentType("application/json;charset=UTF-8");
|
||||||
|
response.getWriter().write(new ObjectMapper().writeValueAsString(
|
||||||
|
BaseResult.error(HttpStatus.FORBIDDEN.value(), "无权访问")));
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 添加JWT过滤器(在UsernamePasswordAuthenticationFilter之前)
|
||||||
|
// 这样JWT请求可以绕过Session认证,直接通过Token认证
|
||||||
|
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SessionRegistry sessionRegistry() {
|
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
|
||||||
return new SessionRegistryImpl();
|
return config.getAuthenticationManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
// 👇 提供 CORS 配置
|
||||||
public ConcurrentSessionFilter concurrentSessionFilter(SessionRegistry sessionRegistry) {
|
|
||||||
return new ConcurrentSessionFilter(sessionRegistry);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 👇 提供 CORS 配置(生产环境请严格限制 origin)
|
|
||||||
@Bean
|
@Bean
|
||||||
@Primary
|
@Primary
|
||||||
public CorsConfigurationSource corsConfigurationSource() {
|
public CorsConfigurationSource corsConfigurationSource() {
|
||||||
CorsConfiguration configuration = new CorsConfiguration();
|
CorsConfiguration configuration = new CorsConfiguration();
|
||||||
|
|
||||||
// ✅ 允许的前端 origin(必须是具体地址,不能 *)
|
// 允许的前端 origin
|
||||||
configuration.setAllowedOriginPatterns(List.of("http://localhost:*", "http://127.0.0.1:*"));
|
configuration.setAllowedOriginPatterns(List.of("http://localhost:*", "http://127.0.0.1:*"));
|
||||||
// ⚠️ 注意:Spring Boot 2.4+ 用 allowedOriginPatterns 支持通配符
|
|
||||||
|
|
||||||
// ✅ 允许凭证(Cookie)
|
// 允许凭证(Cookie)
|
||||||
configuration.setAllowCredentials(true);
|
configuration.setAllowCredentials(true);
|
||||||
|
|
||||||
// 允许的方法和头
|
// 允许的方法和头
|
||||||
@@ -130,27 +153,4 @@ public class SecurityConfig {
|
|||||||
source.registerCorsConfiguration("/**", configuration);
|
source.registerCorsConfiguration("/**", configuration);
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 👇 密码编码器(必须)
|
|
||||||
// @Bean
|
|
||||||
// public PasswordEncoder passwordEncoder() {
|
|
||||||
// return md5PasswordEncoder;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 👇 AuthenticationProvider(用于 DaoAuthenticationProvider)
|
|
||||||
// @Bean
|
|
||||||
// public AuthenticationProvider authenticationProvider() {
|
|
||||||
// DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
|
|
||||||
// authProvider.setUserDetailsService(userDetailsService);
|
|
||||||
// authProvider.setPasswordEncoder(passwordEncoder());
|
|
||||||
// return authProvider;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 👇 AuthenticationManager(用于登录时 authenticate)
|
|
||||||
// @Bean
|
|
||||||
// public AuthenticationManager authenticationManager(
|
|
||||||
// org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration config)
|
|
||||||
// throws Exception {
|
|
||||||
// return config.getAuthenticationManager();
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,28 @@ package com.niuan.erp.module.sys.mapper;
|
|||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.niuan.erp.module.sys.entity.SysUser;
|
import com.niuan.erp.module.sys.entity.SysUser;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface SysUserMapper extends BaseMapper<SysUser> {
|
public interface SysUserMapper extends BaseMapper<SysUser> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据角色ID查询用户ID列表
|
||||||
|
*/
|
||||||
|
@Select("SELECT DISTINCT ur.UserId FROM yy_usersrolemapping ur WHERE ur.RoleId = #{roleId}")
|
||||||
|
List<Long> selectUserIdsByRoleId(@Param("roleId") Long roleId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据权限ID查询用户ID列表
|
||||||
|
*/
|
||||||
|
@Select("""
|
||||||
|
SELECT DISTINCT ur.UserId
|
||||||
|
FROM yy_usersrolemapping ur
|
||||||
|
INNER JOIN yy_rolepermission rp ON ur.RoleId = rp.RoleId
|
||||||
|
WHERE rp.PermissionId = #{permissionId}
|
||||||
|
""")
|
||||||
|
List<Long> selectUserIdsByPermissionId(@Param("permissionId") Long permissionId);
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ package com.niuan.erp.module.sys.service;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.niuan.erp.common.base.BasePageReqParams;
|
import com.niuan.erp.common.base.BasePageReqParams;
|
||||||
|
import com.niuan.erp.common.base.LoginUser;
|
||||||
import com.niuan.erp.module.sys.controller.dto.SysUserDto;
|
import com.niuan.erp.module.sys.controller.dto.SysUserDto;
|
||||||
import com.niuan.erp.module.sys.entity.SysUser;
|
import com.niuan.erp.module.sys.entity.SysUser;
|
||||||
|
|
||||||
@@ -21,4 +22,19 @@ public interface SysUserService {
|
|||||||
void deleteBatch(List<Long> ids);
|
void deleteBatch(List<Long> ids);
|
||||||
|
|
||||||
void setStatus(Long id, Integer status);
|
void setStatus(Long id, Integer status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户ID加载用户信息
|
||||||
|
*/
|
||||||
|
LoginUser loadUserById(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据角色ID查询用户ID列表
|
||||||
|
*/
|
||||||
|
List<Long> getUserIdsByRoleId(Long roleId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据权限ID查询用户ID列表
|
||||||
|
*/
|
||||||
|
List<Long> getUserIdsByPermissionId(Long permissionId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.niuan.erp.common.base.BasePageReqParams;
|
import com.niuan.erp.common.base.BasePageReqParams;
|
||||||
|
import com.niuan.erp.common.security.TokenService;
|
||||||
import com.niuan.erp.common.utils.SecurityUtils;
|
import com.niuan.erp.common.utils.SecurityUtils;
|
||||||
import com.niuan.erp.module.sys.controller.dto.SysRoleDto;
|
import com.niuan.erp.module.sys.controller.dto.SysRoleDto;
|
||||||
import com.niuan.erp.module.sys.controller.dto.SysRoleSelectDto;
|
import com.niuan.erp.module.sys.controller.dto.SysRoleSelectDto;
|
||||||
@@ -15,6 +16,7 @@ import com.niuan.erp.module.sys.mapper.RolePermissionMapper;
|
|||||||
import com.niuan.erp.module.sys.mapper.SysPermissionMapper;
|
import com.niuan.erp.module.sys.mapper.SysPermissionMapper;
|
||||||
import com.niuan.erp.module.sys.mapper.SysRoleMapper;
|
import com.niuan.erp.module.sys.mapper.SysRoleMapper;
|
||||||
import com.niuan.erp.module.sys.service.SysRoleService;
|
import com.niuan.erp.module.sys.service.SysRoleService;
|
||||||
|
import com.niuan.erp.module.sys.service.SysUserService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@@ -34,6 +36,10 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
|||||||
|
|
||||||
private final RolePermissionMapper rolePermissionMapper;
|
private final RolePermissionMapper rolePermissionMapper;
|
||||||
|
|
||||||
|
private final TokenService tokenService;
|
||||||
|
|
||||||
|
private final SysUserService sysUserService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<SysRoleDto> getSysRolePage(BasePageReqParams dto, LambdaQueryWrapper<SysRole> wrapper) {
|
public IPage<SysRoleDto> getSysRolePage(BasePageReqParams dto, LambdaQueryWrapper<SysRole> wrapper) {
|
||||||
IPage<SysRole> result = this.baseMapper.selectPage(new Page<>(dto.page(), dto.pageSize()), wrapper);
|
IPage<SysRole> result = this.baseMapper.selectPage(new Page<>(dto.page(), dto.pageSize()), wrapper);
|
||||||
@@ -87,15 +93,34 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
|||||||
// 使用循环单条插入,避免无主键实体使用批量 insert 方法的问题
|
// 使用循环单条插入,避免无主键实体使用批量 insert 方法的问题
|
||||||
newPermissions.forEach(rolePermissionMapper::insert);
|
newPermissions.forEach(rolePermissionMapper::insert);
|
||||||
}
|
}
|
||||||
|
// 角色修改后,踢掉所有拥有该角色的用户
|
||||||
|
List<Long> userIds = sysUserService.getUserIdsByRoleId(entity.getId());
|
||||||
|
if (userIds != null && !userIds.isEmpty()) {
|
||||||
|
userIds.forEach(tokenService::removeAllUserTokens);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteSysRole(Long id) {
|
public void deleteSysRole(Long id) {
|
||||||
|
// 角色删除前,踢掉所有拥有该角色的用户
|
||||||
|
List<Long> userIds = sysUserService.getUserIdsByRoleId(id);
|
||||||
|
if (userIds != null && !userIds.isEmpty()) {
|
||||||
|
userIds.forEach(tokenService::removeAllUserTokens);
|
||||||
|
}
|
||||||
this.baseMapper.deleteById(id);
|
this.baseMapper.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteBatch(List<Long> ids) {
|
public void deleteBatch(List<Long> ids) {
|
||||||
|
if (ids != null && !ids.isEmpty()) {
|
||||||
|
// 批量删除前,踢掉所有拥有这些角色的用户
|
||||||
|
for (Long roleId : ids) {
|
||||||
|
List<Long> userIds = sysUserService.getUserIdsByRoleId(roleId);
|
||||||
|
if (userIds != null && !userIds.isEmpty()) {
|
||||||
|
userIds.forEach(tokenService::removeAllUserTokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
this.baseMapper.deleteByIds(ids);
|
this.baseMapper.deleteByIds(ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +129,11 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
|||||||
SysRole entity = this.baseMapper.selectById(id);
|
SysRole entity = this.baseMapper.selectById(id);
|
||||||
entity.setStatus(status);
|
entity.setStatus(status);
|
||||||
this.baseMapper.updateById(entity);
|
this.baseMapper.updateById(entity);
|
||||||
|
// 角色状态修改后,踢掉所有拥有该角色的用户
|
||||||
|
List<Long> userIds = sysUserService.getUserIdsByRoleId(id);
|
||||||
|
if (userIds != null && !userIds.isEmpty()) {
|
||||||
|
userIds.forEach(tokenService::removeAllUserTokens);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -5,18 +5,24 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.niuan.erp.common.base.BasePageReqParams;
|
import com.niuan.erp.common.base.BasePageReqParams;
|
||||||
|
import com.niuan.erp.common.base.LoginUser;
|
||||||
import com.niuan.erp.common.exception.BusinessException;
|
import com.niuan.erp.common.exception.BusinessException;
|
||||||
|
import com.niuan.erp.common.security.TokenService;
|
||||||
import com.niuan.erp.common.utils.SecurityUtils;
|
import com.niuan.erp.common.utils.SecurityUtils;
|
||||||
import com.niuan.erp.module.sys.controller.dto.SysUserDto;
|
import com.niuan.erp.module.sys.controller.dto.SysUserDto;
|
||||||
import com.niuan.erp.module.sys.converter.SysUserConverter;
|
import com.niuan.erp.module.sys.converter.SysUserConverter;
|
||||||
|
import com.niuan.erp.module.sys.entity.SysPermission;
|
||||||
import com.niuan.erp.module.sys.entity.SysRole;
|
import com.niuan.erp.module.sys.entity.SysRole;
|
||||||
import com.niuan.erp.module.sys.entity.SysUser;
|
import com.niuan.erp.module.sys.entity.SysUser;
|
||||||
import com.niuan.erp.module.sys.entity.UserRole;
|
import com.niuan.erp.module.sys.entity.UserRole;
|
||||||
|
import com.niuan.erp.module.sys.mapper.SysPermissionMapper;
|
||||||
import com.niuan.erp.module.sys.mapper.SysRoleMapper;
|
import com.niuan.erp.module.sys.mapper.SysRoleMapper;
|
||||||
import com.niuan.erp.module.sys.mapper.SysUserMapper;
|
import com.niuan.erp.module.sys.mapper.SysUserMapper;
|
||||||
import com.niuan.erp.module.sys.mapper.UserRoleMapper;
|
import com.niuan.erp.module.sys.mapper.UserRoleMapper;
|
||||||
import com.niuan.erp.module.sys.service.SysUserService;
|
import com.niuan.erp.module.sys.service.SysUserService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@@ -38,6 +44,10 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
|||||||
|
|
||||||
private final SysRoleMapper sysRoleMapper;
|
private final SysRoleMapper sysRoleMapper;
|
||||||
|
|
||||||
|
private final SysPermissionMapper sysPermissionMapper;
|
||||||
|
|
||||||
|
private final TokenService tokenService;
|
||||||
|
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -175,10 +185,16 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
|||||||
|
|
||||||
// 更新用户角色关联
|
// 更新用户角色关联
|
||||||
updateUserRoles(id, dto.roleIds());
|
updateUserRoles(id, dto.roleIds());
|
||||||
|
|
||||||
|
// 用户修改后,踢掉该用户
|
||||||
|
tokenService.removeAllUserTokens(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteUser(Long id) {
|
public void deleteUser(Long id) {
|
||||||
|
// 用户删除前,踢掉该用户
|
||||||
|
tokenService.removeAllUserTokens(id);
|
||||||
|
|
||||||
// 删除用户角色关联
|
// 删除用户角色关联
|
||||||
LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
|
||||||
wrapper.eq(UserRole::getUserId, id);
|
wrapper.eq(UserRole::getUserId, id);
|
||||||
@@ -194,6 +210,9 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 批量删除前,踢掉这些用户
|
||||||
|
ids.forEach(tokenService::removeAllUserTokens);
|
||||||
|
|
||||||
// 删除用户角色关联
|
// 删除用户角色关联
|
||||||
LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
|
||||||
wrapper.in(UserRole::getUserId, ids);
|
wrapper.in(UserRole::getUserId, ids);
|
||||||
@@ -218,6 +237,9 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
|||||||
updateUser.setUpdateDate(LocalDateTime.now());
|
updateUser.setUpdateDate(LocalDateTime.now());
|
||||||
|
|
||||||
this.baseMapper.updateById(updateUser);
|
this.baseMapper.updateById(updateUser);
|
||||||
|
|
||||||
|
// 用户状态修改后,踢掉该用户
|
||||||
|
tokenService.removeAllUserTokens(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkLoginNameExists(String loginName, Long excludeId) {
|
private void checkLoginNameExists(String loginName, Long excludeId) {
|
||||||
@@ -258,4 +280,35 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
|||||||
// 保存新的角色关联
|
// 保存新的角色关联
|
||||||
saveUserRoles(userId, roleIds);
|
saveUserRoles(userId, roleIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginUser loadUserById(Long userId) {
|
||||||
|
SysUser user = this.baseMapper.selectById(userId);
|
||||||
|
if (user == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SysRole> roleList = sysRoleMapper.selectByUserId(userId);
|
||||||
|
List<SysPermission> permissionList = sysPermissionMapper.selectByUserId(userId);
|
||||||
|
List<GrantedAuthority> authorities = permissionList.stream()
|
||||||
|
.filter(p -> StringUtils.hasText(p.getPermissionCode()))
|
||||||
|
.map(p -> new SimpleGrantedAuthority(p.getPermissionCode()))
|
||||||
|
.collect(Collectors.toUnmodifiableList());
|
||||||
|
|
||||||
|
return LoginUser.builder()
|
||||||
|
.user(user)
|
||||||
|
.roleList(roleList)
|
||||||
|
.authorities(authorities)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Long> getUserIdsByRoleId(Long roleId) {
|
||||||
|
return this.baseMapper.selectUserIdsByRoleId(roleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Long> getUserIdsByPermissionId(Long permissionId) {
|
||||||
|
return this.baseMapper.selectUserIdsByPermissionId(permissionId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,24 @@ spring:
|
|||||||
url: jdbc:mysql://localhost:3306/ai
|
url: jdbc:mysql://localhost:3306/ai
|
||||||
username: steven
|
username: steven
|
||||||
password: 781203
|
password: 781203
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
host: localhost
|
||||||
|
port: 6379
|
||||||
|
password:
|
||||||
|
database: 0
|
||||||
|
lettuce:
|
||||||
|
pool:
|
||||||
|
max-active: 8
|
||||||
|
max-idle: 8
|
||||||
|
min-idle: 0
|
||||||
|
max-wait: 1000ms
|
||||||
|
|
||||||
|
# jwt配置
|
||||||
|
jwt:
|
||||||
|
secret: your-256-bit-secret-key-here-must-be-at-least-32-characters
|
||||||
|
expiration: 86400000
|
||||||
|
refresh-expiration: 604800000
|
||||||
|
|
||||||
# mybatis 设置
|
# mybatis 设置
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
|
|||||||
Reference in New Issue
Block a user