You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
springbootgbu3f/AuthorizationInterceptor.java

200 lines
7.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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 {
// 请求完成后的清理工作,如记录日志等
}
}