Commit 73c193bb by weisong

merge login code to this project

parent a90b10d0
package com.cnooc.expert.common.constant;
/**
* Token的Key常量
*
* @author FuHongZhang
*/
public class TokenConstants
{
/**
* 令牌前缀
* todo 暂时不用
*/
public static final String PREFIX = "Bearer ";
/**
* 令牌秘钥
*/
public final static String SECRET = "DjLRzlhhd/gdx6kPXtfBUSMiL8d0VzDYAtWBKpt3BuA=";
/**
* uuid_key 用于拼接缓存token的key
*/
public static final String UUID_KEY = "uuid_key";
/**
* 用户id
*/
public static final String USER_ID = "user_id";
/**
* 用于拼接缓存token的key
*/
public static final String TOKEN_KEY_ = "TOKEN:TOKEN_KEY_";
}
......@@ -2,21 +2,154 @@ package com.cnooc.expert.common.exception;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.util.Map;
/**
* 通用业务自定义异常
* 适用于参数校验、业务规则冲突、权限校验等所有业务层面的异常场景
*/
@Getter
@Slf4j
public class BusinessException extends RuntimeException {
private final ErrorCode errorCode;
// ------------------- Getter 方法(仅提供获取,不允许修改) -------------------
/**
* 错误码(标准化,如:40001=参数错误,40301=权限不足,50001=系统异常)
* 建议遵循 HTTP 状态码规范,扩展业务子码(如 400 开头为客户端错误,500 开头为服务端错误)
*/
private final Integer errorCode;
/**
* 业务场景标识(如:"user_register"=用户注册,"order_pay"=订单支付,便于定位异常来源)
*/
private final String businessScene;
/**
* 异常关联数据(可选,存储导致异常的关键业务数据,如:{"userId":"10086","phone":"13800138000"})
*/
private final Map<String, Object> errorData;
// ------------------- 构造方法(按场景重载) -------------------
/**
* 基础构造器:仅包含错误信息(适用于简单场景)
* @param message 异常描述信息
*/
public BusinessException(String message) {
super(message);
this.errorCode = 500; // 默认错误码
this.businessScene = "系统错误"; // 默认未知场景
this.errorData = null;
// 记录日志
log.error("BusinessException occurred: {}", message);
}
/**
* 构造器:带错误码+错误信息(最常用,标准化异常响应)
* @param errorCode 错误码(如:"40001")
* @param message 异常描述信息
*/
public BusinessException(Integer errorCode, String message) {
super(message);
this.errorCode = errorCode;
this.businessScene = "UNKNOWN";
this.errorData = null;
// 记录日志
log.error("BusinessException occurred - Code: {}, Message: {}", errorCode, message);
}
/**
* 构造器:带错误码+错误信息+业务场景(精准定位异常来源)
* @param errorCode 错误码
* @param message 异常描述信息
* @param businessScene 业务场景(如:"user_login")
*/
public BusinessException(Integer errorCode, String message, String businessScene) {
super(message);
this.errorCode = errorCode;
this.businessScene = businessScene;
this.errorData = null;
// 记录日志
log.error("BusinessException occurred - Code: {}, Scene: {}, Message: {}",
errorCode, businessScene, message);
}
public BusinessException(ErrorCode errorCode) {
super(errorCode.getMsg());
/**
* 构造器:带完整信息(错误码+信息+场景+异常数据,便于排查)
* @param errorCode 错误码
* @param message 异常描述信息
* @param businessScene 业务场景
* @param errorData 异常关联数据(如错误的参数、用户ID等)
*/
public BusinessException(Integer errorCode, String message, String businessScene, Map<String, Object> errorData) {
super(message);
this.errorCode = errorCode;
this.businessScene = businessScene;
this.errorData = errorData;
// 记录日志
log.error("BusinessException occurred - Code: {}, Scene: {}, Message: {}, Data: {}",
errorCode, businessScene, message, errorData);
}
/**
* 构造器:带根因异常(用于包装底层异常,如SQL异常、IO异常)
* @param errorCode 错误码
* @param message 异常描述信息
* @param cause 根因异常(底层原始异常)
*/
public BusinessException(Integer errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
this.businessScene = "UNKNOWN";
this.errorData = null;
// 记录日志
log.error("BusinessException occurred - Code: {}, Message: {}, Cause: {}",
errorCode, message, cause.getClass().getSimpleName());
}
public BusinessException(ErrorCode errorCode, String message) {
super(StringUtils.defaultIfBlank(message, errorCode.getMsg()));
/**
* 构造器:全参数(适配复杂场景)
* @param errorCode 错误码
* @param message 异常描述信息
* @param businessScene 业务场景
* @param errorData 异常关联数据
* @param cause 根因异常
*/
public BusinessException(Integer errorCode, String message, String businessScene, Map<String, Object> errorData, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
this.businessScene = businessScene;
this.errorData = errorData;
// 记录日志
log.error("BusinessException occurred - Code: {}, Scene: {}, Message: {}, Data: {}, Cause: {}",
errorCode, businessScene, message, errorData, cause.getClass().getSimpleName());
}
// ------------------- 增强异常信息可读性 -------------------
@Override
public String toString() {
StringBuilder sb = new StringBuilder("BusinessException: [");
sb.append("errorCode=").append(errorCode)
.append(", businessScene=").append(businessScene)
.append(", message=").append(getMessage()).append("]");
// 追加异常数据(若存在)
if (errorData != null && !errorData.isEmpty()) {
sb.append(" | errorData=").append(errorData);
}
// 追加根因异常(若存在)
if (getCause() != null) {
sb.append(" | cause=").append(getCause().getClass().getSimpleName());
}
return sb.toString();
}
}
}
\ No newline at end of file
package com.cnooc.expert.common.interceptor;
import com.cnooc.expert.common.constant.TokenConstants;
import com.cnooc.expert.common.exception.BusinessException;
import com.cnooc.expert.common.exception.GlobalErrorCodeConstants;
import com.cnooc.expert.common.utils.JwtUtils;
import com.cnooc.expert.common.utils.UserUtils;
import com.cnooc.expert.common.utils.ValidUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* @author: FuHongZhang
* @date 2025-10-16 10:22
*/
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (token == null || token.trim().isEmpty()) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
throw new BusinessException(HttpServletResponse.SC_UNAUTHORIZED, GlobalErrorCodeConstants.LOGIN_EXPIRED.getMsg());
}
try {
Map<String, String> userMap = JwtUtils.getTokenInfo(token);
String userId = userMap.get(TokenConstants.USER_ID);
String uuidKey = userMap.get(TokenConstants.UUID_KEY);
ValidUtils.isNotNull(uuidKey, "登录异常,请重新登录");
// String cachedToken = redisTemplate.opsForValue().get(TokenConstants.TOKEN_KEY_ + userId);
//
// if (cachedToken == null || !cachedToken.equals(token)) {
// // token不存在或不匹配,说明已退出登录或token失效
// response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
// response.getWriter().write("{\"code\":401,\"msg\":\"请先登录\"}");
// return false;
// }
// todo 通过token解析出用户id,代用UserUtils.setUserId(userId)方法进行存储
// 所有条件都满足,放行请求
return true;
} catch (Exception e) {
log.info("token解析异常 {}",e.getMessage(),e);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
UserUtils.clear();
throw new BusinessException(401, "请先登录");
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserUtils.clear();
}
}
package com.cnooc.expert.common.utils;
import com.alibaba.fastjson.JSON;
import com.cnooc.expertmanageminiapp.common.constant.TokenConstants;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* JWT工具类,用于生成、解析和验证token
* @author FuHongZhang
*/
@Slf4j
public class JwtUtils {
// 密钥,实际项目中应放在配置文件中
private static final String SECRET_KEY = TokenConstants.SECRET;
// Token过期时间,这里设置为48小时
private static final long EXPIRATION_TIME = 48 * 60 * 60 * 1000;
// private static final long EXPIRATION_TIME = 3 * 1000;
/**
* 生成token
* @return token字符串
*/
public static String createToken(Integer userId , String uuidKey) {
// 生成token
return Jwts.builder()
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.setIssuedAt(new Date())
.claim(TokenConstants.UUID_KEY, uuidKey)
.claim(TokenConstants.USER_ID, userId)
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
/**
* 从token中获取用于拼接缓存的key uuid_key 和用户 user_id
*/
public static Map<String, String> getTokenInfo(String token) {
// 如果过期,会抛Exception
Map<String, String> userInfo = new HashMap<>();
Claims claims = extractAllClaims(token);
userInfo.put(TokenConstants.UUID_KEY, (String) claims.get(TokenConstants.UUID_KEY));
userInfo.put(TokenConstants.USER_ID, claims.get(TokenConstants.USER_ID) + "");
return userInfo;
}
/**
*
* @param token
* @return
*/
private static Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
}
public static void main(String[] args) throws Exception{
String portalToken = JwtUtils.createToken(111, "testtest");
Thread.sleep(5000);
Map<String, String> claims = JwtUtils.getTokenInfo(portalToken);
System.out.print(JSON.toJSONString(claims, true));
}
}
package com.cnooc.expert.common.utils;
import com.cnooc.expertmanageminiapp.system.entity.pojo.ZhuanJiaUser;
import org.springframework.stereotype.Component;
/**
* 用户id获取工具类
* @author: FuHongZhang
* @date 2025-10-24 11:14:07
*/
@Component("userUtils")
public class UserUtils {
public static ZhuanJiaUser getCurrentUser() {
ZhuanJiaUser zhuanJiaUser = USER_INFO.get();
if (zhuanJiaUser != null) {
return zhuanJiaUser;
}
return null;
}
private static final ThreadLocal<ZhuanJiaUser> USER_INFO = new ThreadLocal<>();
public static void setUserId(ZhuanJiaUser zhuanJiaUser) {
USER_INFO.set(zhuanJiaUser);
}
public static ZhuanJiaUser getUserId() {
return USER_INFO.get();
}
public static void clear() {
USER_INFO.remove();
}
}
\ No newline at end of file
package com.cnooc.expert.common.utils;
import com.cnooc.expert.common.exception.BusinessException;
import com.cnooc.expert.common.exception.ErrorCode;
import com.cnooc.expert.common.exception.GlobalErrorCodeConstants;
import java.util.Collection;
/**
* @Author: FuHongZhang
* @CreateTime: 2025-09-18 16:11
*/
public class ValidUtils {
/**
* 校验字符串是否为空
* @param text 待校验的字符串
* @param message 异常信息
*/
public static void isText(String text, String message) {
if (text == null || text.trim().isEmpty()) {
throw new BusinessException(GlobalErrorCodeConstants.PARAM_FORMAT_ERROR.getCode(),message);
}
}
/**
* 校验对象是否为null
* @param obj 待校验的对象
* @param message 异常信息
*/
public static void isNotNull(Object obj, String message) {
if (obj == null) {
throw new BusinessException(GlobalErrorCodeConstants.PARAM_FORMAT_ERROR.getCode(),message);
}
}
/**
* 自定义错误码及错误信息
* @param obj
* @param errorCode
*/
public static void isNotNull(Object obj, ErrorCode errorCode) {
if (obj == null) {
throw new BusinessException(errorCode.getCode(), errorCode.getMsg());
}
}
/**
* 校验集合是否为空
* @param collection 待校验的集合
* @param message 异常信息
*/
public static void isNullList(Collection<?> collection, String message) {
if (collection == null || collection.isEmpty()) {
throw new BusinessException(GlobalErrorCodeConstants.PARAM_FORMAT_ERROR.getCode(),message);
}
}
/**
* 校验表达式是否为true
* @param expression 待校验的布尔表达式
* @param message 异常信息
*/
public static void isTrue(boolean expression, String message) {
if (!expression) {
throw new BusinessException(GlobalErrorCodeConstants.PARAM_FORMAT_ERROR.getCode(),message);
}
}
/**
* 校验表达式是否为true
* @param expression
* @param errorCode
*/
public static void isTrue(boolean expression, ErrorCode errorCode) {
if (!expression) {
throw new BusinessException(errorCode.getCode(), errorCode.getMsg());
}
}
/**
* 校验表达式是否为false
* @param expression 待校验的布尔表达式
* @param message 异常信息
*/
public static void isFalse(boolean expression, String message) {
if (expression) {
throw new BusinessException(GlobalErrorCodeConstants.PARAM_FORMAT_ERROR.getCode(),message);
}
}
}
package com.cnooc.expert.config;
import com.cnooc.expert.common.interceptor.LoginInterceptor;
import com.cnooc.expert.common.interceptor.WebAuthInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
......@@ -9,8 +11,22 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class TheWebMvcConfigurer implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册登录拦截器,并设置拦截路径和排除路径
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") // 拦截所有路径
.excludePathPatterns( // 排除一些路径
"/sys/**",
"/verify/**",
"/person/**",
"/text/**"
);
registry.addInterceptor(webAuthInterceptor());
}
......
package com.cnooc.expert.system.entity.pojo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author: FuHongZhang
* @date 2025-10-30 18:00
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ZhuanJiaUser {
private static final long serialVersionUID = -1L;
private String zhuanJiaGuid;
private String zhuanJiaShiXiangGuid;
private String zhuanJiaName;
private String zhuanJiaCode;
private String shenFenZheng;
private String mobile;
private Short zhuanJiaZhuangTai;
private Boolean shiFouYiFaZhuanJia;
private String suoShuBuMeng;//所在部门
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment