package com.ruoyi.framework.web.service; import com.ruoyi.common.constant.CacheConstants; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.core.service.IConfigService; import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.ip.AddressUtils; import com.ruoyi.common.utils.ip.IpUtils; import com.ruoyi.common.utils.uuid.IdUtils; import eu.bitwalker.useragentutils.UserAgent; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; /** * token验证处理 * * @author ruoyi */ @Slf4j @Component public class TokenService { // 令牌自定义标识 @Value("${token.header}") private String header; // 令牌秘钥 @Value("${token.secret}") private String secret; // 令牌有效期(默认30分钟) @Value("${token.expireTime}") private int expireTime; protected static final long MILLIS_SECOND = 1000; protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; @Autowired private RedisCache redisCache; @Autowired private IConfigService configService; /** * 获取用户身份信息 * * @return 用户信息 */ public LoginUser getLoginUser(HttpServletRequest request) { // 获取请求携带的令牌 String token = getToken(request); if (StringUtils.isNotEmpty(token)) { try { Claims claims = parseToken(token); // 解析对应的权限以及用户信息 String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); String userKey = getTokenKey(uuid); LoginUser user = redisCache.getCacheObject(userKey); // 统一在会话解析层处理「全院区权限」,避免各业务接口直接读 user.campusid 时绕过配置。 applyCampusAuthority(user); return user; } catch (Exception e) { } } return null; } /** * 应用全院区权限。 *
* {@code sys.authority.campus.all} 配置的是用户名列表,命中后把当前会话的 * campusid 置空;后续所有按 campusid 过滤的 SQL 都会因为参数为空而跳过院区条件。 *
* 这里每次读取会话时都按当前配置重新判断,所以不需要重新登录;同时兼容配置值
* 中带空格、换行、中文逗号/分号的情况。
*/
private void applyCampusAuthority(LoginUser loginUser) {
if (loginUser == null || loginUser.getUser() == null) {
return;
}
try {
String allCampus = configService.selectConfigByKey("sys.authority.campus.all");
if (StringUtils.isEmpty(allCampus)) {
return;
}
String userName = loginUser.getUser().getUserName();
if (StringUtils.isEmpty(userName)) {
return;
}
String[] users = allCampus.split("[,,;;\\s]+");
for (String item : users) {
if (userName.equals(item.trim())) {
loginUser.getUser().setCampusid(null);
return;
}
}
} catch (Exception e) {
// 配置服务异常不能导致 token 解析失败,否则会把用户直接踢下线。
log.warn("【全院区权限】校验 sys.authority.campus.all 失败,userId={}", loginUser.getUserId(), e);
}
}
/**
* 设置用户身份信息
*/
public void setLoginUser(LoginUser loginUser) {
if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) {
refreshToken(loginUser);
}
}
/**
* 删除用户身份信息
*/
public void delLoginUser(String token) {
if (StringUtils.isNotEmpty(token)) {
String userKey = getTokenKey(token);
redisCache.deleteObject(userKey);
}
}
/**
* 创建令牌
*
* @param loginUser 用户信息
* @return 令牌
*/
public String createToken(LoginUser loginUser) {
String token = IdUtils.fastUUID();
loginUser.setToken(token);
setUserAgent(loginUser);
refreshToken(loginUser);
Map