first commit

This commit is contained in:
starrySky
2024-03-20 09:28:04 +08:00
commit 989f0210f2
286 changed files with 25129 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
package com.starry.admin.common.aspect;
import cn.hutool.core.convert.Convert;
import com.starry.admin.common.domain.LoginUser;
import com.starry.admin.modules.system.entity.SysRoleEntity;
import com.starry.admin.modules.system.entity.SysUserEntity;
import com.starry.admin.utils.SecurityUtils;
import com.starry.common.annotation.DataScope;
import com.starry.common.context.SecurityContextHolder;
import com.starry.common.domain.BaseEntity;
import com.starry.common.utils.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* 数据过滤处理
*
* @author vctgo
*/
@Aspect
@Component
public class DataScopeAspect {
/**
* 全部数据权限
*/
public static final String DATA_SCOPE_ALL = "1";
/**
* 自定数据权限
*/
public static final String DATA_SCOPE_CUSTOM = "2";
/**
* 部门数据权限
*/
public static final String DATA_SCOPE_DEPT = "3";
/**
* 部门及以下数据权限
*/
public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
/**
* 仅本人数据权限
*/
public static final String DATA_SCOPE_SELF = "5";
/**
* 数据权限过滤关键字
*/
public static final String DATA_SCOPE = "dataScope";
/**
* 数据范围过滤
*
* @param joinPoint 切点
* @param user 用户
* @param deptAlias 部门别名
* @param userAlias 用户别名
* @param permission 权限字符
*/
public static void dataScopeFilter(JoinPoint joinPoint, SysUserEntity user, String deptAlias, String userAlias, String permission) {
StringBuilder sqlString = new StringBuilder();
List<String> conditions = new ArrayList<>();
for (SysRoleEntity role : user.getRoles()) {
String dataScope = role.getDataScope();
if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)) {
continue;
}
if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions())
&& !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) {
continue;
}
if (DATA_SCOPE_ALL.equals(dataScope)) {
sqlString = new StringBuilder();
break;
} else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
role.getRoleId()));
} else if (DATA_SCOPE_DEPT.equals(dataScope)) {
sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
} else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
deptAlias, user.getDeptId(), user.getDeptId()));
} else if (DATA_SCOPE_SELF.equals(dataScope)) {
if (StringUtils.isNotBlank(userAlias)) {
sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
} else {
// 数据权限为仅本人且没有userAlias别名不查询任何数据
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
}
}
conditions.add(dataScope);
}
if (StringUtils.isNotBlank(sqlString.toString())) {
Object params = joinPoint.getArgs()[0];
if (StringUtils.isNotNull(params) && params instanceof BaseEntity) {
BaseEntity baseEntity = (BaseEntity) params;
baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
}
}
}
@Before("@annotation(controllerDataScope)")
public void doBefore(JoinPoint point, DataScope controllerDataScope) {
clearDataScope(point);
handleDataScope(point, controllerDataScope);
}
protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) {
if (controllerDataScope == null) {
return;
}
// 获取当前的用户
LoginUser loginUser = SecurityUtils.getLoginUser();
if (StringUtils.isNotNull(loginUser)) {
SysUserEntity currentUser = loginUser.getUser();
// 如果是超级管理员,则不过滤数据
if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) {
String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), SecurityContextHolder.getPermission());
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
controllerDataScope.userAlias(), permission);
}
}
}
/**
* 拼接权限sql前先清空params.dataScope参数防止注入
*/
private void clearDataScope(final JoinPoint joinPoint) {
Object params = joinPoint.getArgs()[0];
if (StringUtils.isNotNull(params) && params instanceof BaseEntity) {
BaseEntity baseEntity = (BaseEntity) params;
baseEntity.getParams().put(DATA_SCOPE, "");
}
}
}

View File

@@ -0,0 +1,168 @@
package com.starry.admin.common.aspect;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson2.JSON;
import com.starry.admin.common.domain.LoginUser;
import com.starry.admin.modules.system.entity.SysOperationLogEntity;
import com.starry.admin.modules.system.service.ISysOperationLogService;
import com.starry.admin.utils.SecurityUtils;
import com.starry.common.annotation.Log;
import com.starry.common.utils.ServletUtils;
import com.starry.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
/**
* @author admin
*/
@Aspect
@Component
@Slf4j
public class LogAspect {
@Resource
private ISysOperationLogService operLogService;
/**
* 处理完请求后执行
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
public void doAfterReturn(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
handleLog(joinPoint, controllerLog, jsonResult);
}
protected void handleLog(final JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
try {
// 获取当前的用户
LoginUser loginUser = SecurityUtils.getLoginUser();
// 日志记录
SysOperationLogEntity operLog = new SysOperationLogEntity();
operLog.setStatus(0);
// 请求的IP地址
String iP = ServletUtil.getClientIP(ServletUtils.getRequest());
if ("0:0:0:0:0:0:0:1".equals(iP)) {
iP = "127.0.0.1";
}
operLog.setOperIp(iP);
operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
if (loginUser != null) {
operLog.setOperName(loginUser.getUsername());
}
if (null != null) {
operLog.setStatus(1);
operLog.setErrorMsg(StringUtils.substring(((Exception) null).getMessage(), 0, 2000));
}
// 设置方法名称
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
operLog.setMethod(className + "." + methodName + "()");
operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
operLog.setOperTime(new Date());
// 处理设置注解上的参数
getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
// 保存数据库
operLogService.save(operLog);
} catch (Exception exp) {
log.error("异常信息:{}", exp.getMessage());
}
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
* @param log 日志
* @param operLog 操作日志
*/
public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperationLogEntity operLog, Object jsonResult) {
// 设置操作业务类型
operLog.setBusinessType(log.businessType().ordinal());
// 设置标题
operLog.setTitle(log.title());
// 是否需要保存request参数和值
if (log.isSaveRequestData()) {
// 设置参数的信息
setRequestValue(joinPoint, operLog);
}
// 是否需要保存response参数和值
if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) {
operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
}
}
/**
* 获取请求的参数放到log中
*
* @param operLog 操作日志
*/
private void setRequestValue(JoinPoint joinPoint, SysOperationLogEntity operLog) {
String requsetMethod = operLog.getRequestMethod();
if (HttpMethod.PUT.name().equals(requsetMethod) || HttpMethod.POST.name().equals(requsetMethod)) {
String params = argsArrayToString(joinPoint.getArgs());
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
} else {
Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
}
}
/**
* 参数拼装
*/
private String argsArrayToString(Object[] paramsArray) {
StringBuilder params = new StringBuilder();
if (paramsArray != null) {
for (Object object : paramsArray) {
// 不为空 并且是不需要过滤的 对象
if (StringUtils.isNotNull(object) && !isFilterObject(object)) {
Object jsonObj = JSON.toJSON(object);
params.append(jsonObj.toString()).append(" ");
}
}
}
return params.toString().trim();
}
/**
* 判断是否需要过滤的对象。
*
* @param object 对象信息。
* @return 如果是需要过滤的对象则返回true否则返回false。
*/
@SuppressWarnings("rawtypes")
public boolean isFilterObject(final Object object) {
Class<?> clazz = object.getClass();
if (clazz.isArray()) {
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
} else if (Collection.class.isAssignableFrom(clazz)) {
Collection collection = (Collection) object;
for (Object value : collection) {
return value instanceof MultipartFile;
}
} else if (Map.class.isAssignableFrom(clazz)) {
Map map = (Map) object;
for (Object value : map.entrySet()) {
Map.Entry entry = (Map.Entry) value;
return entry.getValue() instanceof MultipartFile;
}
}
return object instanceof MultipartFile || object instanceof HttpServletRequest || object instanceof HttpServletResponse || object instanceof BindingResult;
}
}

View File

@@ -0,0 +1,338 @@
package com.starry.admin.common.component;
import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.starry.admin.common.domain.LoginUser;
import com.starry.admin.common.security.entity.JwtUser;
import com.starry.common.constant.CacheConstants;
import com.starry.common.constant.Constants;
import com.starry.common.constant.SecurityConstants;
import com.starry.common.redis.RedisCache;
import com.starry.common.utils.ServletUtils;
import com.starry.common.utils.StringUtils;
import com.starry.common.utils.ip.AddressUtils;
import com.starry.common.utils.ip.IpUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author admin
* token 组件
* @since 2021/9/6
*/
@Slf4j
@Component
public class JwtToken {
private static final String CLAIM_KEY_USERNAME = "sub";
private static final String CLAIM_KEY_CREATED = "created";
private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expire;
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Resource
private RedisCache redisCache;
/**
* 从token中获取登录用户名
*/
public String getUserNameFromToken(String token) {
String username;
try {
Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
/**
* 校验token
*/
public boolean validateToken(String token, UserDetails userDetails) {
String username = getUserNameFromToken(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
/**
* 根据用户信息生成token
*/
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
/**
* 创建令牌
*
* @param jwtUser 用户信息
* @return 令牌
*/
public String createToken(JwtUser jwtUser) {
String token = IdUtil.fastSimpleUUID();
jwtUser.setToken(token);
setUserAgent(jwtUser);
refresToken(jwtUser);
Map<String, Object> claims = new HashMap<>();
claims.put(Constants.LOGIN_USER_KEY, token);
return createToken(claims);
}
/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
private String createToken(Map<String, Object> claims) {
String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
return token;
}
/**
* 判断token是否已经失效
*/
private boolean isTokenExpired(String token) {
Date expiredDate = getClaimsFromToken(token).getExpiration();
return expiredDate.before(new Date());
}
private String generateToken(Map<String, Object> claims) {
return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate())
// 签名算法
.signWith(SignatureAlgorithm.HS512, secret).compact();
}
/**
* 生成token的过期时间
*/
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expire * 1000);
}
/**
* 从令牌中获取数据声明
*
* @param token 令牌
* @return 数据声明
*/
private Claims getClaimsFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
} catch (Exception e) {
log.info("JWT格式验证失败:{}", token);
}
return claims;
}
/**
* 设置用户代理信息
*
* @param jwtUser 登录信息
*/
public void setUserAgent(JwtUser jwtUser) {
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
String ip = ServletUtil.getClientIP(ServletUtils.getRequest());
jwtUser.setIpaddr(ip);
jwtUser.setLoginLocation(AddressUtils.getRealAddressByIp(ip));
jwtUser.setBrowser(userAgent.getBrowser().getName());
jwtUser.setOs(userAgent.getOs().getName());
}
public void refresToken(JwtUser jwtUser) {
jwtUser.setLoginTime(System.currentTimeMillis());
jwtUser.setExpireTime(jwtUser.getLoginTime() + expire * 1000);
String userKey = getTokenKey(jwtUser.getToken());
redisCache.setCacheObject(userKey, jwtUser, expire, TimeUnit.SECONDS);
}
private String getTokenKey(String uuid) {
return CacheConstants.LOGIN_TOKEN_KEY + uuid;
}
/**
* 获取登录用户身份信息
*
* @return 用户信息
*/
public JwtUser getLoginUser(HttpServletRequest request) {
String token = getToken(request);
if (StringUtils.isNotEmpty(token)) {
try {
Claims claims = getClaimsFromToken(token);
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
String userKey = getTokenKey(uuid);
JwtUser user = redisCache.getCacheObject(userKey);
return user;
} catch (Exception e) {
}
}
return null;
}
/**
* 获取请求token
*
* @param request
* @return token
*/
private String getToken(HttpServletRequest request) {
// 获取请求头
String token = request.getHeader(tokenHeader);
if (StringUtils.isNotEmpty(token) && token.startsWith(tokenHead)) {
token = token.replace(tokenHead, "");
}
return token;
}
/**
* 验证令牌有效期相差不足20分钟自动刷新缓存
*
* @param jwtUser
* @return 令牌
*/
public void verifyToken(JwtUser jwtUser) {
long expireTime = jwtUser.getExpireTime();
long currentTime = System.currentTimeMillis();
if (expireTime - currentTime <= MILLIS_MINUTE_TEN) {
refresToken(jwtUser);
}
}
/**
* 删除用户身份信息
*/
public void removeJwtUser(String token) {
if (StringUtils.isNotEmpty(token)) {
String userKey = getTokenKey(token);
redisCache.deleteObject(userKey);
}
}
/**
* 创建令牌
*/
public Map<String, Object> createToken(LoginUser loginUser) {
String token = IdUtil.fastSimpleUUID();
String userId = loginUser.getUser().getUserId();
String userName = loginUser.getUser().getUserCode();
String tenantId = loginUser.getUser().getTenantId();
Long deptId = loginUser.getUser().getDeptId();
loginUser.setToken(token);
loginUser.setUserId(userId);
loginUser.setUserName(userName);
loginUser.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
// 添加地址信息
setUserAgent(loginUser);
refreshToken(loginUser);
// Jwt存储信息
Map<String, Object> claimsMap = new HashMap<>(8);
claimsMap.put(SecurityConstants.USER_KEY, token);
claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName);
// 租户id
claimsMap.put(SecurityConstants.DETAILS_TENANT_ID, tenantId);
// 部门id
claimsMap.put(SecurityConstants.DETAILS_DEPT_ID, deptId);
// 接口返回信息
Map<String, Object> rspMap = new HashMap<>();
rspMap.put("token", this.createToken(claimsMap));
rspMap.put("expires_in", expire);
rspMap.put("tenant_id", tenantId);
rspMap.put("tokenHead", tokenHead);
return rspMap;
}
/**
* 设置用户代理信息
*
* @param loginUser 登录信息
*/
public void setUserAgent(LoginUser loginUser) {
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
String ip = ServletUtil.getClientIP(ServletUtils.getRequest());
loginUser.setIpaddr(ip);
loginUser.setLoginLocation(AddressUtils.getRealAddressByIp(ip));
loginUser.setBrowser(userAgent.getBrowser().getName());
loginUser.setOs(userAgent.getOs().getName());
}
/**
* 刷新令牌有效期
*
* @param loginUser 登录信息
*/
public void refreshToken(LoginUser loginUser) {
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expire * 1000);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
redisCache.setCacheObject(userKey, loginUser, expire, TimeUnit.MINUTES);
}
/**
* 获取登录用户身份信息
*
* @return 用户信息
*/
public LoginUser getNewLoginUser(HttpServletRequest request) {
String token = getToken(request);
if (StringUtils.isNotEmpty(token)) {
try {
Claims claims = getClaimsFromToken(token);
String uuid = (String) claims.get(SecurityConstants.USER_KEY);
String userKey = getTokenKey(uuid);
LoginUser loginUser = redisCache.getCacheObject(userKey);
com.starry.common.context.SecurityContextHolder.set(SecurityConstants.DETAILS_TENANT_ID, loginUser.getUser().getTenantId());
com.starry.common.context.SecurityContextHolder.set(SecurityConstants.LOGIN_USER, loginUser);
return loginUser;
} catch (Exception e) {
}
}
return null;
}
/**
* 验证令牌有效期相差不足20分钟自动刷新缓存
*
* @param loginUser
* @return 令牌
*/
public void verifyToken(LoginUser loginUser) {
long expireTime = loginUser.getExpireTime();
long currentTime = System.currentTimeMillis();
if (expireTime - currentTime <= MILLIS_MINUTE_TEN) {
refreshToken(loginUser);
}
}
}

View File

@@ -0,0 +1,149 @@
package com.starry.admin.common.component;
import com.starry.admin.common.domain.LoginUser;
import com.starry.admin.modules.system.entity.SysRoleEntity;
import com.starry.admin.utils.SecurityUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.Set;
/**
* 自定义权限实现ss取自SpringSecurity首字母
*
* @author admin
*/
@Service("customSs")
public class PermissionService {
/**
* 所有权限标识
*/
private static final String ALL_PERMISSION = "*:*:*";
/**
* 管理员角色权限标识
*/
private static final String SUPER_ADMIN = "admin";
private static final String ROLE_DELIMETER = ",";
private static final String PERMISSION_DELIMETER = ",";
/**
* 验证用户是否具备某权限
*
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public boolean hasPermi(String permission) {
if (StringUtils.isEmpty(permission)) {
return false;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null || CollectionUtils.isEmpty(loginUser.getPermissions())) {
return false;
}
return hasPermissions(loginUser.getPermissions(), permission);
}
/**
* 验证用户是否不具备某权限,与 hasPermi逻辑相反
*
* @param permission 权限字符串
* @return 用户是否不具备某权限
*/
public boolean lacksPermi(String permission) {
return hasPermi(permission) != true;
}
/**
* 验证用户是否具有以下任意一个权限
*
* @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表
* @return 用户是否具有以下任意一个权限
*/
public boolean hasAnyPermi(String permissions) {
if (StringUtils.isEmpty(permissions)) {
return false;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null || CollectionUtils.isEmpty(loginUser.getPermissions())) {
return false;
}
Set<String> authorities = loginUser.getPermissions();
for (String permission : permissions.split(PERMISSION_DELIMETER)) {
if (permission != null && hasPermissions(authorities, permission)) {
return true;
}
}
return false;
}
/**
* 判断用户是否拥有某个角色
*
* @param role 角色字符串
* @return 用户是否具备某角色
*/
public boolean hasRole(String role) {
if (StringUtils.isEmpty(role)) {
return false;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) {
return false;
}
for (SysRoleEntity sysRoleEntity : loginUser.getUser().getRoles()) {
String roleKey = sysRoleEntity.getRoleKey();
if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) {
return true;
}
}
return false;
}
/**
* 验证用户是否不具备某角色,与 isRole逻辑相反。
*
* @param role 角色名称
* @return 用户是否不具备某角色
*/
public boolean lacksRole(String role) {
return hasRole(role) != true;
}
/**
* 验证用户是否具有以下任意一个角色
*
* @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表
* @return 用户是否具有以下任意一个角色
*/
public boolean hasAnyRoles(String roles) {
if (StringUtils.isEmpty(roles)) {
return false;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) {
return false;
}
for (String role : roles.split(ROLE_DELIMETER)) {
if (hasRole(role)) {
return true;
}
}
return false;
}
/**
* 判断是否包含权限
*
* @param permissions 权限列表
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
private boolean hasPermissions(Set<String> permissions, String permission) {
return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
}
}

View File

@@ -0,0 +1,133 @@
package com.starry.admin.common.domain;
import com.starry.admin.modules.system.entity.SysUserEntity;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Date;
import java.util.Set;
/**
* @author admin
*/
@Data
public class LoginUser implements UserDetails {
private static final long serialVersionUID = 1L;
/**
* 用户唯一标识
*/
private String token;
/**
* 用户名id
*/
private String userId;
/**
* 用户名
*/
private String userName;
/**
* 登录时间
*/
private Long loginTime;
/**
* 过期时间
*/
private Long expireTime;
/**
* 登录IP地址
*/
private String ipaddr;
/**
* 登录地点
*/
private String loginLocation;
/**
* 浏览器类型
*/
private String browser;
/**
* 操作系统
*/
private String os;
/**
* 权限列表
*/
private Set<String> permissions;
/**
* 角色列表
*/
private Set<String> roles;
/**
* 用户信息
*/
private SysUserEntity user;
/**
* 租户租赁截止日期--dhr
*/
private Date tenantEndDate;
/**
* 租户状态
*/
private Integer tenantStatus;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return user.getPassWord();
}
@Override
public String getUsername() {
return user.getUserCode();
}
/**
* 账户是否未过期
**/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 账户是否未锁定
**/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 密码是否未过期
**/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 账户是否激活
**/
@Override
public boolean isEnabled() {
return user.getStatus() == 0;
}
}

View File

@@ -0,0 +1,46 @@
package com.starry.admin.common.domain;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.starry.admin.modules.system.entity.SysDeptEntity;
import com.starry.admin.modules.system.entity.SysMenuEntity;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author Treeselect树结构实体类
* @since 2022/7/4
*/
@Data
public class TreeSelect implements Serializable {
/**
* 节点ID
*/
private Long id;
/**
* 节点名称
*/
private String label;
/**
* 子节点
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<TreeSelect> children;
public TreeSelect(SysMenuEntity menu) {
this.id = menu.getMenuId();
this.label = menu.getMenuName();
this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
}
public TreeSelect(SysDeptEntity dept) {
this.id = dept.getDeptId();
this.label = dept.getDeptName();
this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,10 @@
package com.starry.admin.common.exception;
/**
* 自定义异常处理器 * * @author admin
*/
public class CustomException extends RuntimeException {
public CustomException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,63 @@
package com.starry.admin.common.exception;
/**
* @author 业务异常
* @since 2023/3/9
*/
public class ServiceException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 错误码
*/
private Integer code;
/**
* 错误提示
*/
private String message;
/**
* 错误明细,内部调试错误
*/
private String detailMessage;
/**
* 空构造方法,避免反序列化问题
*/
public ServiceException() {
}
public ServiceException(String message) {
this.message = message;
}
public ServiceException(String message, Integer code) {
this.message = message;
this.code = code;
}
public String getDetailMessage() {
return detailMessage;
}
public ServiceException setDetailMessage(String detailMessage) {
this.detailMessage = detailMessage;
return this;
}
@Override
public String getMessage() {
return message;
}
public ServiceException setMessage(String message) {
this.message = message;
return this;
}
public Integer getCode() {
return code;
}
}

View File

@@ -0,0 +1,71 @@
package com.starry.admin.common.exception.handler;
import com.starry.admin.common.exception.CustomException;
import com.starry.admin.common.exception.ServiceException;
import com.starry.common.result.R;
import com.starry.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
/**
* @author 全局异常处理
* @since 2023/3/9
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 业务异常
*/
@ExceptionHandler(ServiceException.class)
public R handleServiceException(ServiceException e, HttpServletRequest request) {
log.error(e.getMessage(), e);
Integer code = e.getCode();
return StringUtils.isNotNull(code) ? R.error(code, e.getMessage()) : R.error(e.getMessage());
}
/**
* 拦截未知的运行时异常
*/
@ExceptionHandler(RuntimeException.class)
public R handleRuntimeException(RuntimeException e, HttpServletRequest request) {
String requestUrl = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestUrl, e);
return R.error("系统发生未知异常,请联系管理员");
}
/**
* 系统异常
*/
@ExceptionHandler(Exception.class)
public R handleException(Exception e, HttpServletRequest request) {
String requestUrl = request.getRequestURI();
log.error("请求地址'{}',发生系统异常.", requestUrl, e);
return R.error("系统出现内部错误,请联系管理员");
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public R methodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
BindingResult bindingResult = e.getBindingResult();
StringBuilder errorMessageBuilder = new StringBuilder();
for (FieldError error : bindingResult.getFieldErrors()) {
errorMessageBuilder.append(error.getDefaultMessage());
}
return R.error("请求参数异常," + errorMessageBuilder);
}
@ExceptionHandler(CustomException.class)
public R customException(CustomException e, HttpServletRequest request) {
return R.error(e.getMessage());
}
}

View File

@@ -0,0 +1,79 @@
package com.starry.admin.common.mybatis.config;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.starry.admin.common.mybatis.handler.MyTenantLineHandler;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
/**
* @author admin
*/
@Configuration
@MapperScan("com.starry.**.mapper")
@EnableTransactionManagement
public class MybatisPlusConfig {
/**
* druid注入
*
* @return dataSource
*/
@Bean(name = "dataSource")
@ConfigurationProperties(prefix = "spring.datasource.druid")
public DataSource dataSource() {
return DruidDataSourceBuilder.create().build();
}
/**
* 配置事物管理器
*
* @return DataSourceTransactionManager
*/
@Bean(name = "transactionManager")
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 租户插件
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new MyTenantLineHandler()));
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 乐观锁插件
interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
// 阻断插件
interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
return interceptor;
}
/**
* 乐观锁
*/
@Bean
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
return new OptimisticLockerInnerInterceptor();
}
/**
* 防止全表更新与删除
*/
@Bean
public BlockAttackInnerInterceptor blockAttackInnerInterceptor() {
return new BlockAttackInnerInterceptor();
}
}

View File

@@ -0,0 +1,45 @@
package com.starry.admin.common.mybatis.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.starry.admin.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* @author admin
* 字段默认值处理类
* @since 2021/9/1
*/
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.setFieldValByName("createdTime", new Date(), metaObject);
this.setFieldValByName("deleted", false, metaObject);
this.setFieldValByName("version", 1L, metaObject);
Object createUser = this.getFieldValByName("createdBy", metaObject);
if (createUser == null) {
if (SecurityUtils.isLogin()) {
this.setFieldValByName("createdBy", SecurityUtils.getUserId(), metaObject);
}
}
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.setFieldValByName("updatedTime", new Date(), metaObject);
Object createUser = this.getFieldValByName("updatedBy", metaObject);
if (createUser == null) {
if (SecurityUtils.isLogin()) {
this.setFieldValByName("updatedBy", SecurityUtils.getUserId(), metaObject);
}
}
}
}

View File

@@ -0,0 +1,54 @@
package com.starry.admin.common.mybatis.handler;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.starry.admin.utils.SecurityUtils;
import com.starry.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.NullValue;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* @author admin
* 多租户处理器
* @since 2023/3/7
*/
@Slf4j
@Component
public class MyTenantLineHandler implements TenantLineHandler {
/**
* 排除过滤的表
*/
private static final String[] TABLE_FILTER = {"sys_menu", "sys_tenant_package", "sys_tenant", "sys_dict", "sys_dict_data"};
/**
* 排除过滤的表前缀
*/
private static final String[] TABLE_PRE = {"qrtz", "gen"};
@Override
public Expression getTenantId() {
// 取出当前请求的服务商ID通过解析器注入到SQL中。
Long tenantId = SecurityUtils.getTenantId();
if (tenantId == null) {
return new NullValue();
}
return new LongValue(tenantId);
}
/**
* 跳过不需要加多租户的表
*/
@Override
public boolean ignoreTable(String tableName) {
String prefix = StringUtils.substringBefore(tableName, "_");
if (Arrays.asList(TABLE_FILTER).contains(tableName) || Arrays.asList(TABLE_PRE).contains(prefix)) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,124 @@
package com.starry.admin.common.security.config;
import com.starry.admin.common.security.entity.JwtUser;
import com.starry.admin.common.security.filter.JwtAuthenticationTokenFilter;
import com.starry.admin.common.security.handler.CustomAccessDeniedHandler;
import com.starry.admin.common.security.handler.CustomAuthenticationEntryPoint;
import com.starry.admin.common.security.handler.CustomLogoutSuccessHandler;
import com.starry.admin.modules.system.entity.SysUserEntity;
import com.starry.admin.modules.system.service.SysMenuService;
import com.starry.admin.modules.system.service.SysUserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.annotation.Resource;
import java.util.Set;
/**
* @author admin
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private CustomAccessDeniedHandler customAccessDeniedHandler;
@Resource
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
@Resource
private SysUserService sysUserService;
@Resource
private SysMenuService menuService;
@Resource
private CustomLogoutSuccessHandler customLogoutSuccessHandler;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()// 由于使用的是JWT我们这里不需要csrf
.sessionManagement()// 基于token所以不需要session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 允许对于网站静态资源的无授权访问
.antMatchers(HttpMethod.GET,
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/swagger-resources/**",
"/v2/api-docs/**"
).permitAll()
// 对登录注册要允许匿名访问
.antMatchers("/login", "/captcha/get-captcha").permitAll()
// 跨域请求会先进行一次options请求
.antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest()// 除上面外的所有请求全部需要鉴权认证
.authenticated();
// 禁用缓存
httpSecurity.headers().cacheControl();
// 添加Logout filter
httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(customLogoutSuccessHandler);
// 添加JWT filter
httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
// 添加自定义未授权和未登录结果返回
httpSecurity.exceptionHandling()
.accessDeniedHandler(customAccessDeniedHandler)
.authenticationEntryPoint(customAuthenticationEntryPoint);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
@Bean
@Override
public UserDetailsService userDetailsService() {
// 获取登录用户信息
return username -> {
SysUserEntity user = sysUserService.selectUserByUserName(username);
if (user != null) {
if (user.getStatus() == 1) {
throw new UsernameNotFoundException("对不起,您的账号:" + username + " 已停用");
}
// 获取菜单权限
Set<String> permissionList = menuService.selectMenuPermsByUserId(user.getUserId());
return new JwtUser(user, permissionList);
}
throw new UsernameNotFoundException("用户名或密码错误");
};
}
/**
* 装载BCrypt密码编码器
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* JWT filter
*/
@Bean
public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
return new JwtAuthenticationTokenFilter();
}
}

View File

@@ -0,0 +1,202 @@
package com.starry.admin.common.security.entity;
import com.starry.admin.modules.system.entity.SysUserEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Set;
/**
* @author admin
*/
public class JwtUser implements UserDetails {
/**
* 用户ID
*/
private Long userId;
/**
* 登录时间
*/
private Long loginTime;
/**
* 用户唯一标识
*/
private String token;
/**
* 过期时间
*/
private Long expireTime;
/**
* 登录IP地址
*/
private String ipaddr;
/**
* 登录地点
*/
private String loginLocation;
/**
* 浏览器类型
*/
private String browser;
/**
* 操作系统
*/
private String os;
/**
* 用户信息
*/
private SysUserEntity user;
/**
* 权限列表
*/
private Set<String> permissions;
public JwtUser(SysUserEntity user, Set<String> permissions) {
this.user = user;
this.permissions = permissions;
}
public JwtUser(Long userId, SysUserEntity user, Set<String> permissions) {
this.userId = userId;
this.user = user;
this.permissions = permissions;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return user.getPassWord();
}
@Override
public String getUsername() {
return user.getUserCode();
}
/**
* 账户是否未过期
**/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 账户是否未锁定
**/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 密码是否未过期
**/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 账户是否激活
**/
@Override
public boolean isEnabled() {
return user.getStatus() == 0;
}
public Long getLoginTime() {
return loginTime;
}
public void setLoginTime(Long loginTime) {
this.loginTime = loginTime;
}
public Set<String> getPermissions() {
return permissions;
}
public void setPermissions(Set<String> permissions) {
this.permissions = permissions;
}
public SysUserEntity getUser() {
return user;
}
public void setUser(SysUserEntity user) {
this.user = user;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getIpaddr() {
return ipaddr;
}
public void setIpaddr(String ipaddr) {
this.ipaddr = ipaddr;
}
public String getLoginLocation() {
return loginLocation;
}
public void setLoginLocation(String loginLocation) {
this.loginLocation = loginLocation;
}
public String getBrowser() {
return browser;
}
public void setBrowser(String browser) {
this.browser = browser;
}
public String getOs() {
return os;
}
public void setOs(String os) {
this.os = os;
}
public Long getExpireTime() {
return expireTime;
}
public void setExpireTime(Long expireTime) {
this.expireTime = expireTime;
}
}

View File

@@ -0,0 +1,47 @@
package com.starry.admin.common.security.filter;
import com.starry.admin.common.component.JwtToken;
import com.starry.admin.common.domain.LoginUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author admin
* JWT登录授权过滤器
* @since 2021/9/6
*/
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Resource
private JwtToken jwtToken;
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
LoginUser jwtUser = jwtToken.getNewLoginUser(httpServletRequest);
if (null != jwtUser && null == SecurityContextHolder.getContext().getAuthentication()) {
jwtToken.verifyToken(jwtUser);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(jwtUser, null, jwtUser.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}

View File

@@ -0,0 +1,30 @@
package com.starry.admin.common.security.handler;
import cn.hutool.json.JSONUtil;
import com.starry.common.result.R;
import com.starry.common.result.ResultCodeEnum;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author admin
* 当访问接口没有权限时,自定义的返回结果
* @since 2021/9/6
*/
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json");
httpServletResponse.getWriter().println(JSONUtil.parse(R.error(ResultCodeEnum.FORBIDDEN.getCode(), ResultCodeEnum.FORBIDDEN.getMessage())));
httpServletResponse.getWriter().flush();
}
}

View File

@@ -0,0 +1,29 @@
package com.starry.admin.common.security.handler;
import cn.hutool.json.JSONUtil;
import com.starry.common.result.R;
import com.starry.common.result.ResultCodeEnum;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author admin
* 当未登录或者token失效访问接口时自定义的返回结果
* @since 2021/9/6
*/
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json");
httpServletResponse.getWriter().println(JSONUtil.parse(R.error(ResultCodeEnum.UNAUTHORIZED.getCode(), ResultCodeEnum.UNAUTHORIZED.getMessage())));
httpServletResponse.getWriter().flush();
}
}

View File

@@ -0,0 +1,41 @@
package com.starry.admin.common.security.handler;
import com.alibaba.fastjson2.JSON;
import com.starry.admin.common.component.JwtToken;
import com.starry.admin.common.security.entity.JwtUser;
import com.starry.admin.manager.AsyncManager;
import com.starry.admin.manager.factory.AsyncFactory;
import com.starry.common.constant.Constants;
import com.starry.common.result.R;
import com.starry.common.utils.ServletUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author admin
* 自定义退出处理类 返回成功
* @since 2022/7/8
*/
@Component
public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
@Resource
private JwtToken jwtToken;
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
JwtUser jwtUser = jwtToken.getLoginUser(httpServletRequest);
if (null != jwtUser) {
// 删除用户缓存记录
jwtToken.removeJwtUser(jwtUser.getToken());
// 记录用户退出日志
AsyncManager.me().execute(AsyncFactory.recordLoginLog(jwtUser.getUsername(), Constants.LOGOUT, "退出成功"));
}
ServletUtils.renderString(httpServletResponse, JSON.toJSONString(R.ok("退出成功")));
}
}