refactor: 优化Security登录

This commit is contained in:
Bunny 2025-02-17 22:54:54 +08:00
parent 08aeca4834
commit 27147998aa
14 changed files with 76 additions and 95 deletions

View File

@ -23,6 +23,11 @@
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
</dependency> </dependency>
<!-- spring-security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- mybatis-plus --> <!-- mybatis-plus -->
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>

View File

@ -6,6 +6,11 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
/** /**
* <p> * <p>
@ -20,7 +25,7 @@ import lombok.experimental.Accessors;
@Accessors(chain = true) @Accessors(chain = true)
@TableName("sys_user") @TableName("sys_user")
@Schema(name = "AdminUser对象", title = "用户信息", description = "用户信息") @Schema(name = "AdminUser对象", title = "用户信息", description = "用户信息")
public class AdminUser extends BaseEntity { public class AdminUser extends BaseEntity implements UserDetails {
@Schema(name = "username", title = "用户名") @Schema(name = "username", title = "用户名")
private String username; private String username;
@ -55,5 +60,9 @@ public class AdminUser extends BaseEntity {
@Schema(name = "status", title = "状态", description = "1:禁用 0:正常") @Schema(name = "status", title = "状态", description = "1:禁用 0:正常")
private Boolean status; private Boolean status;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of();
}
} }

View File

@ -1,11 +1,11 @@
package cn.bunny.services.security.config; package cn.bunny.services.security.config;
import cn.bunny.services.security.custom.CustomPasswordEncoder; import cn.bunny.services.security.custom.CustomPasswordEncoder;
import cn.bunny.services.security.custom.service.CustomUserDetailsService;
import cn.bunny.services.security.custom.service.impl.CustomAuthorizationManagerServiceImpl;
import cn.bunny.services.security.filter.TokenLoginFilterService; import cn.bunny.services.security.filter.TokenLoginFilterService;
import cn.bunny.services.security.handelr.SecurityAccessDeniedHandler; import cn.bunny.services.security.handelr.SecurityAccessDeniedHandler;
import cn.bunny.services.security.handelr.SecurityAuthenticationEntryPoint; import cn.bunny.services.security.handelr.SecurityAuthenticationEntryPoint;
import cn.bunny.services.security.service.CustomUserDetailsService;
import cn.bunny.services.security.service.impl.CustomAuthorizationManagerServiceImpl;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;

View File

@ -1,51 +0,0 @@
package cn.bunny.services.security.custom;
import cn.bunny.common.service.context.BaseContext;
import cn.bunny.dao.entity.system.AdminUser;
import java.util.List;
/**
* 检查是否是管理员
*/
public class CustomCheckIsAdmin {
/**
* 判断是否是管理员
*
* @param roleList 角色代码列表
* @return 是否是管理员
*/
public static boolean checkAdmin(List<String> roleList) {
// 判断是否是超级管理员
if (BaseContext.getUserId().equals(1L)) return true;
// 判断是否是 admin
return roleList.stream().anyMatch(role -> role.equals("admin"));
}
/**
* 判断是否是管理员
*
* @param roleList 角色代码列表
* @param permissions 权限列表
* @param adminUser 用户信息
* @return 是否是管理员
*/
public static boolean checkAdmin(List<String> roleList, List<String> permissions, AdminUser adminUser) {
// 判断是否是超级管理员
boolean isIdAdmin = adminUser.getId().equals(1L);
boolean isAdmin = roleList.stream().anyMatch(role -> role.equals("admin"));
// 判断是否是 admin
if (isIdAdmin || isAdmin) {
roleList.add("admin");
permissions.add("*");
permissions.add("*::*");
permissions.add("*::*::*");
return true;
}
return false;
}
}

View File

@ -1,23 +0,0 @@
package cn.bunny.services.security.custom;
import cn.bunny.dao.entity.system.AdminUser;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
/**
* 重写自带的User
*/
@Getter
@Setter
public class CustomUser extends User {
private AdminUser user;
public CustomUser(AdminUser user, Collection<? extends GrantedAuthority> authorities) {
super(user.getUsername(), user.getPassword(), authorities);
this.user = user;
}
}

View File

@ -1,4 +1,4 @@
package cn.bunny.services.security.service; package cn.bunny.services.security.custom.service;
import cn.bunny.dao.dto.system.user.LoginDto; import cn.bunny.dao.dto.system.user.LoginDto;
import cn.bunny.dao.vo.system.user.LoginVo; import cn.bunny.dao.vo.system.user.LoginVo;

View File

@ -1,4 +1,4 @@
package cn.bunny.services.security.service.impl; package cn.bunny.services.security.custom.service.impl;
import cn.bunny.common.service.context.BaseContext; import cn.bunny.common.service.context.BaseContext;
import cn.bunny.common.service.utils.JwtHelper; import cn.bunny.common.service.utils.JwtHelper;
@ -10,7 +10,7 @@ import cn.bunny.dao.vo.system.user.LoginVo;
import cn.bunny.services.mapper.PowerMapper; import cn.bunny.services.mapper.PowerMapper;
import cn.bunny.services.mapper.RoleMapper; import cn.bunny.services.mapper.RoleMapper;
import cn.bunny.services.security.custom.CustomAuthenticationException; import cn.bunny.services.security.custom.CustomAuthenticationException;
import cn.bunny.services.security.custom.CustomCheckIsAdmin; import cn.bunny.services.utils.RoleUtil;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -103,7 +103,7 @@ public class CustomAuthorizationManagerServiceImpl implements AuthorizationManag
List<String> roleCodeList = roleList.stream().map(Role::getRoleCode).toList(); List<String> roleCodeList = roleList.stream().map(Role::getRoleCode).toList();
// 判断是否是管理员用户 // 判断是否是管理员用户
boolean checkedAdmin = CustomCheckIsAdmin.checkAdmin(roleCodeList); boolean checkedAdmin = RoleUtil.checkAdmin(roleCodeList);
if (checkedAdmin) return true; if (checkedAdmin) return true;
// 判断请求地址是否是 noManage 不需要被验证的 // 判断请求地址是否是 noManage 不需要被验证的

View File

@ -1,4 +1,4 @@
package cn.bunny.services.security.service.impl; package cn.bunny.services.security.custom.service.impl;
import cn.bunny.common.service.exception.AuthCustomerException; import cn.bunny.common.service.exception.AuthCustomerException;
import cn.bunny.dao.dto.system.user.LoginDto; import cn.bunny.dao.dto.system.user.LoginDto;
@ -8,7 +8,7 @@ import cn.bunny.dao.vo.result.Result;
import cn.bunny.dao.vo.result.ResultCodeEnum; import cn.bunny.dao.vo.result.ResultCodeEnum;
import cn.bunny.dao.vo.system.user.LoginVo; import cn.bunny.dao.vo.system.user.LoginVo;
import cn.bunny.services.mapper.UserMapper; import cn.bunny.services.mapper.UserMapper;
import cn.bunny.services.security.custom.CustomUser; import cn.bunny.services.security.custom.service.CustomUserDetailsService;
import cn.bunny.services.utils.UserUtil; import cn.bunny.services.utils.UserUtil;
import cn.bunny.services.utils.login.DefaultLoginStrategy; import cn.bunny.services.utils.login.DefaultLoginStrategy;
import cn.bunny.services.utils.login.EmailLoginStrategy; import cn.bunny.services.utils.login.EmailLoginStrategy;
@ -18,7 +18,6 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -30,7 +29,7 @@ import java.util.HashMap;
import static cn.bunny.common.service.utils.ResponseUtil.out; import static cn.bunny.common.service.utils.ResponseUtil.out;
@Component @Component
public class CustomUserDetailsServiceImpl implements cn.bunny.services.security.service.CustomUserDetailsService { public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
@Autowired @Autowired
private RedisTemplate<String, Object> redisTemplate; private RedisTemplate<String, Object> redisTemplate;
@ -58,7 +57,8 @@ public class CustomUserDetailsServiceImpl implements cn.bunny.services.security.
AdminUser adminUser = userMapper.selectOne(queryWrapper); AdminUser adminUser = userMapper.selectOne(queryWrapper);
if (adminUser == null) throw new UsernameNotFoundException(ResultCodeEnum.USER_IS_EMPTY.getMessage()); if (adminUser == null) throw new UsernameNotFoundException(ResultCodeEnum.USER_IS_EMPTY.getMessage());
return new CustomUser(adminUser, AuthorityUtils.createAuthorityList()); // return new CustomUser(adminUser, AuthorityUtils.createAuthorityList());
return adminUser;
} }
/** /**

View File

@ -6,9 +6,9 @@ import cn.bunny.dao.enums.LoginEnums;
import cn.bunny.dao.vo.result.Result; import cn.bunny.dao.vo.result.Result;
import cn.bunny.dao.vo.result.ResultCodeEnum; import cn.bunny.dao.vo.result.ResultCodeEnum;
import cn.bunny.dao.vo.system.user.LoginVo; import cn.bunny.dao.vo.system.user.LoginVo;
import cn.bunny.services.security.custom.service.CustomUserDetailsService;
import cn.bunny.services.security.handelr.SecurityAuthenticationFailureHandler; import cn.bunny.services.security.handelr.SecurityAuthenticationFailureHandler;
import cn.bunny.services.security.handelr.SecurityAuthenticationSuccessHandler; import cn.bunny.services.security.handelr.SecurityAuthenticationSuccessHandler;
import cn.bunny.services.security.service.CustomUserDetailsService;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain; import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
@ -27,8 +27,8 @@ import java.io.IOException;
import static cn.bunny.common.service.utils.ResponseUtil.out; import static cn.bunny.common.service.utils.ResponseUtil.out;
/** /**
* * UsernamePasswordAuthenticationFilter * UsernamePasswordAuthenticationFilter
* * 也可以在这里添加验证码短信等的验证 * 也可以在这里添加验证码短信等的验证
* 由于SpringSecurity的登录只能是表单形式 并且用户名密码需要时usernamepassword,可以通过继承 UsernamePasswordAuthenticationFilter 获取登录请求的参数 * 由于SpringSecurity的登录只能是表单形式 并且用户名密码需要时usernamepassword,可以通过继承 UsernamePasswordAuthenticationFilter 获取登录请求的参数
* 再去设置到 UsernamePasswordAuthenticationToken 来改变请求传参方式参数名等 或者也可以在登录的时候加入其他参数等等 * 再去设置到 UsernamePasswordAuthenticationToken 来改变请求传参方式参数名等 或者也可以在登录的时候加入其他参数等等
*/ */

View File

@ -7,11 +7,12 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
/** /**
* 没有权限访问 * 没有权限访问
*/ */
public class SecurityAccessDeniedHandler implements org.springframework.security.web.access.AccessDeniedHandler { public class SecurityAccessDeniedHandler implements AccessDeniedHandler {
@SneakyThrows @SneakyThrows
@Override @Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) { public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) {

View File

@ -5,10 +5,11 @@ import com.alibaba.fastjson2.JSON;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import java.io.IOException; import java.io.IOException;
public class SecurityAuthenticationFailureHandler implements org.springframework.security.web.authentication.AuthenticationFailureHandler { public class SecurityAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override @Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException { public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
// 错误消息 // 错误消息

View File

@ -19,8 +19,8 @@ import cn.bunny.services.mapper.RoleMapper;
import cn.bunny.services.mapper.RolePowerMapper; import cn.bunny.services.mapper.RolePowerMapper;
import cn.bunny.services.mapper.RouterMapper; import cn.bunny.services.mapper.RouterMapper;
import cn.bunny.services.mapper.RouterRoleMapper; import cn.bunny.services.mapper.RouterRoleMapper;
import cn.bunny.services.security.custom.CustomCheckIsAdmin;
import cn.bunny.services.service.RouterService; import cn.bunny.services.service.RouterService;
import cn.bunny.services.utils.RoleUtil;
import cn.bunny.services.utils.RouterServiceUtil; import cn.bunny.services.utils.RouterServiceUtil;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@ -84,7 +84,7 @@ public class RouterServiceImpl extends ServiceImpl<RouterMapper, Router> impleme
List<UserRouterVo> list = new ArrayList<>(); List<UserRouterVo> list = new ArrayList<>();
// 查询用户角色判断是否是管理员角色 // 查询用户角色判断是否是管理员角色
boolean isAdmin = CustomCheckIsAdmin.checkAdmin(userRoleCodeList); boolean isAdmin = RoleUtil.checkAdmin(userRoleCodeList);
// 查询路由和角色对应关系 // 查询路由和角色对应关系
List<ViewRouterRole> routerRoleList = routerRoleMapper.viewRouterRolesWithAll(); List<ViewRouterRole> routerRoleList = routerRoleMapper.viewRouterRolesWithAll();

View File

@ -1,5 +1,6 @@
package cn.bunny.services.utils; package cn.bunny.services.utils;
import cn.bunny.common.service.context.BaseContext;
import cn.bunny.dao.constant.RedisUserConstant; import cn.bunny.dao.constant.RedisUserConstant;
import cn.bunny.dao.entity.system.AdminUser; import cn.bunny.dao.entity.system.AdminUser;
import cn.bunny.services.mapper.UserMapper; import cn.bunny.services.mapper.UserMapper;
@ -22,6 +23,45 @@ public class RoleUtil {
@Autowired @Autowired
private UserUtil userUtil; private UserUtil userUtil;
/**
* 判断是否是管理员
*
* @param roleList 角色代码列表
* @param permissions 权限列表
* @param adminUser 用户信息
* @return 是否是管理员
*/
public static boolean checkAdmin(List<String> roleList, List<String> permissions, AdminUser adminUser) {
// 判断是否是超级管理员
boolean isIdAdmin = adminUser.getId().equals(1L);
boolean isAdmin = roleList.stream().anyMatch(role -> role.equals("admin"));
// 判断是否是 admin
if (isIdAdmin || isAdmin) {
roleList.add("admin");
permissions.add("*");
permissions.add("*::*");
permissions.add("*::*::*");
return true;
}
return false;
}
/**
* 判断是否是管理员
*
* @param roleList 角色代码列表
* @return 是否是管理员
*/
public static boolean checkAdmin(List<String> roleList) {
// 判断是否是超级管理员
if (BaseContext.getUserId().equals(1L)) return true;
// 判断是否是 admin
return roleList.stream().anyMatch(role -> role.equals("admin"));
}
/** /**
* 批量更新Redis中用户信息 * 批量更新Redis中用户信息
* *

View File

@ -16,7 +16,6 @@ import cn.bunny.services.mapper.PowerMapper;
import cn.bunny.services.mapper.RoleMapper; import cn.bunny.services.mapper.RoleMapper;
import cn.bunny.services.mapper.UserLoginLogMapper; import cn.bunny.services.mapper.UserLoginLogMapper;
import cn.bunny.services.mapper.UserMapper; import cn.bunny.services.mapper.UserMapper;
import cn.bunny.services.security.custom.CustomCheckIsAdmin;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -132,7 +131,7 @@ public class UserUtil {
List<String> permissions = new ArrayList<>(); List<String> permissions = new ArrayList<>();
// 判断是否是 admin 如果是admin 赋予所有权限 // 判断是否是 admin 如果是admin 赋予所有权限
boolean isAdmin = CustomCheckIsAdmin.checkAdmin(roles, permissions, user); boolean isAdmin = RoleUtil.checkAdmin(roles, permissions, user);
if (!isAdmin) { if (!isAdmin) {
permissions = powerMapper.selectListByUserId(userId).stream().map(Power::getPowerCode).toList(); permissions = powerMapper.selectListByUserId(userId).stream().map(Power::getPowerCode).toList();
} }