|
|
package com.interceptor;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
import java.io.PrintWriter;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.Map;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
import org.springframework.web.method.HandlerMethod;
|
|
|
import org.springframework.web.servlet.HandlerInterceptor;
|
|
|
import org.springframework.web.bind.annotation.RequestMethod;
|
|
|
import org.springframework.http.HttpStatus;
|
|
|
|
|
|
import com.annotation.IgnoreAuth;
|
|
|
import com.entity.EIException;
|
|
|
import com.entity.TokenEntity;
|
|
|
import com.service.TokenService;
|
|
|
import com.utils.R;
|
|
|
|
|
|
/**
|
|
|
* 权限(Token)验证拦截器
|
|
|
*
|
|
|
* 核心职责:实现基于Token的用户认证和权限验证
|
|
|
*
|
|
|
* 功能特性:
|
|
|
* 1. Token验证:验证用户Token的有效性
|
|
|
* 2. 跨域支持:处理跨域请求和OPTIONS预检请求
|
|
|
* 3. 注解支持:通过@IgnoreAuth注解跳过验证
|
|
|
* 4. 会话管理:将用户信息存入Session
|
|
|
* 5. 统一响应:标准化的错误响应格式
|
|
|
*
|
|
|
* 技术实现:
|
|
|
* - Spring MVC拦截器
|
|
|
* - 注解驱动配置
|
|
|
* - FastJSON序列化
|
|
|
* - 会话管理
|
|
|
*/
|
|
|
@Component // 声明为Spring组件,由容器管理
|
|
|
public class AuthorizationInterceptor implements HandlerInterceptor {
|
|
|
|
|
|
// Token在请求头中的键名
|
|
|
public static final String LOGIN_TOKEN_KEY = "Token";
|
|
|
|
|
|
@Autowired
|
|
|
private TokenService tokenService;
|
|
|
|
|
|
/**
|
|
|
* 预处理方法,在控制器方法执行前调用
|
|
|
*
|
|
|
* @param request HTTP请求对象
|
|
|
* @param response HTTP响应对象
|
|
|
* @param handler 处理器对象
|
|
|
* @return 是否继续执行后续处理
|
|
|
* @throws Exception 处理过程中可能抛出的异常
|
|
|
*/
|
|
|
@Override
|
|
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
|
|
|
|
|
// 支持跨域请求 - 设置CORS响应头
|
|
|
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
|
|
|
response.setHeader("Access-Control-Max-Age", "3600"); // 预检请求缓存时间
|
|
|
response.setHeader("Access-Control-Allow-Credentials", "true"); // 允许携带凭证
|
|
|
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,request-source,Token, Origin,imgType, Content-Type, cache-control,postman-token,Cookie, Accept,authorization");
|
|
|
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); // 允许的源
|
|
|
|
|
|
// 跨域时会首先发送一个OPTIONS请求,这里我们给OPTIONS请求直接返回正常状态
|
|
|
if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
|
|
|
response.setStatus(HttpStatus.OK.value()); // 返回200状态码
|
|
|
return false; // 不继续执行后续处理
|
|
|
}
|
|
|
|
|
|
// 检查处理器方法上的@IgnoreAuth注解
|
|
|
IgnoreAuth annotation;
|
|
|
if (handler instanceof HandlerMethod) {
|
|
|
// 获取方法上的@IgnoreAuth注解
|
|
|
annotation = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuth.class);
|
|
|
} else {
|
|
|
// 如果不是HandlerMethod(如资源处理器),直接放行
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
// 从header中获取token
|
|
|
String token = request.getHeader(LOGIN_TOKEN_KEY);
|
|
|
|
|
|
/**
|
|
|
* 不需要验证权限的方法直接放过
|
|
|
*/
|
|
|
if(annotation != null) {
|
|
|
return true; // 有@IgnoreAuth注解,跳过Token验证
|
|
|
}
|
|
|
|
|
|
// Token验证逻辑
|
|
|
TokenEntity tokenEntity = null;
|
|
|
if(StringUtils.isNotBlank(token)) {
|
|
|
// 通过TokenService验证Token有效性
|
|
|
tokenEntity = tokenService.getTokenEntity(token);
|
|
|
}
|
|
|
|
|
|
// 如果Token有效
|
|
|
if(tokenEntity != null) {
|
|
|
// 将用户信息存入Session,供后续使用
|
|
|
request.getSession().setAttribute("userId", tokenEntity.getUserid()); // 用户ID
|
|
|
request.getSession().setAttribute("role", tokenEntity.getRole()); // 用户角色
|
|
|
request.getSession().setAttribute("tableName", tokenEntity.getTablename()); // 用户表名
|
|
|
request.getSession().setAttribute("username", tokenEntity.getUsername()); // 用户名
|
|
|
return true; // 验证通过,继续执行
|
|
|
}
|
|
|
|
|
|
// Token无效或未提供Token,返回未授权错误
|
|
|
PrintWriter writer = null;
|
|
|
response.setCharacterEncoding("UTF-8"); // 设置响应编码
|
|
|
response.setContentType("application/json; charset=utf-8"); // 设置响应类型
|
|
|
try {
|
|
|
writer = response.getWriter();
|
|
|
// 返回JSON格式的错误响应
|
|
|
writer.print(JSONObject.toJSONString(R.error(401, "请先登录")));
|
|
|
} finally {
|
|
|
// 确保Writer正确关闭
|
|
|
if(writer != null){
|
|
|
writer.close();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 也可以选择抛出异常的方式(注释掉的代码)
|
|
|
// throw new EIException("请先登录", 401);
|
|
|
|
|
|
return false; // 验证失败,中断请求处理
|
|
|
}
|
|
|
|
|
|
// 可以添加的增强方法:
|
|
|
|
|
|
/**
|
|
|
* 获取当前登录用户ID
|
|
|
*
|
|
|
* @param request HTTP请求
|
|
|
* @return 用户ID,如果未登录返回null
|
|
|
*/
|
|
|
public static Long getUserId(HttpServletRequest request) {
|
|
|
Object userId = request.getSession().getAttribute("userId");
|
|
|
return userId != null ? (Long) userId : null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取当前登录用户角色
|
|
|
*
|
|
|
* @param request HTTP请求
|
|
|
* @return 用户角色,如果未登录返回null
|
|
|
*/
|
|
|
public static String getRole(HttpServletRequest request) {
|
|
|
Object role = request.getSession().getAttribute("role");
|
|
|
return role != null ? (String) role : null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取当前登录用户名
|
|
|
*
|
|
|
* @param request HTTP请求
|
|
|
* @return 用户名,如果未登录返回null
|
|
|
*/
|
|
|
public static String getUsername(HttpServletRequest request) {
|
|
|
Object username = request.getSession().getAttribute("username");
|
|
|
return username != null ? (String) username : null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 检查当前用户是否具有指定角色
|
|
|
*
|
|
|
* @param request HTTP请求
|
|
|
* @param role 要检查的角色
|
|
|
* @return 是否具有指定角色
|
|
|
*/
|
|
|
public static boolean hasRole(HttpServletRequest request, String role) {
|
|
|
String currentRole = getRole(request);
|
|
|
return currentRole != null && currentRole.equals(role);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 检查当前用户是否已登录
|
|
|
*
|
|
|
* @param request HTTP请求
|
|
|
* @return 是否已登录
|
|
|
*/
|
|
|
public static boolean isLogin(HttpServletRequest request) {
|
|
|
return getUserId(request) != null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 后处理方法(可选实现)
|
|
|
*/
|
|
|
@Override
|
|
|
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
|
|
|
Object handler, Exception ex) throws Exception {
|
|
|
// 请求完成后的清理工作,如记录日志等
|
|
|
}
|
|
|
} |