登陆接口:生成jwt
拦截器:先判断有无token,token有效性,都通过的话,将jwt中的信息放入threadlocal中,否则拒绝访问
其它页面:从threadlocal中取出数据
1 pom.xml
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.26</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.3.0</version>
</dependency>
2 JWT 工具类编写
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtils {
private static final String KEY = "%$^%$&$%HGHFDSFSDGFDHDHD";
//接收业务数据,生成token并返回
// withClaim 配置有效载荷
// withExpiresAt 配置过期时间
// sign 配置加密算法和密钥
public static String genToken(Map<String, Object> claims) {
return JWT.create()
.withClaim("claims", claims)
.withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))
.sign(Algorithm.HMAC256(KEY));
}
//接收token,验证token,并返回业务数据
public static Map<String, Object> parseToken(String token) {
return JWT.require(Algorithm.HMAC256(KEY))
.build()
.verify(token)
.getClaim("claims")
.asMap();
}
}
3 Threadlocal工具类
import java.util.Map;
public class ThreadLocalUtil {
private static ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<>();
public static void set(Map<String, Object> data){
threadLocal.set(data);
}
public static Map<String, Object> get(){
return threadLocal.get();
}
//防止内存泄漏
public static void remove(){
threadLocal.remove();
}
}
4 拦截器编写
import com.example.demo_7.util.JwtUtils;
import com.example.demo_7.util.ThreadLocalUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* 拦截器
*/
@Component
@Order(1)
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
/**
* 在请求处理之前进行调用(Controller方法调用之前)
* true:放行
* false:拦截
*
* token 有效性校验 (有没有towen, 是否过期,是否被篡改,是否被伪造等)
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("request.getRequestURI().toString() = " + request.getRequestURI().toString());
response.setContentType("application/json;charset=utf-8");
String token = request.getHeader("Authorization"); // 假设Token放在Authorization头中
// 直接检查token是否存在,不存在则立即返回false并发送错误响应
if (!StringUtils.hasText(token)) {
sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, "没有携带Token,请在请求头中添加Token。");
return false;
}
Map<String, Object> stringObjectMap=null;
try {
stringObjectMap = JwtUtils.parseToken(token); // 验证并解析Token
if (stringObjectMap == null) {
// Token无效,返回false并发送错误响应
sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, "无效的Token。");
return false;
}
} catch (Exception e) {
log.error("Token验证过程中发生错误", e);
sendErrorResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Token验证过程中发生错误。");
return false; // 发生异常也返回false
}
// 如果以上验证都通过,则将用户信息存入ThreadLocal,并在最后返回true以放行请求
ThreadLocalUtil.set(stringObjectMap);
return true;
}
/**
* 发送错误响应
* @param response
* @param statusCode
* @param message
*/
private void sendErrorResponse(HttpServletResponse response, int statusCode, String message) throws Exception {
response.setStatus(statusCode);
response.getWriter().write(message);
}
/**
* 在请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 清除 ThreadLocal 中的数据,切记不要忘了,否则会有内存泄漏的风险
ThreadLocalUtil.remove();
}
}
5 拦截器配置
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
/**
* 配置拦截器
* @param registry 相当于拦截器的注册中心
* addPathPatterns 配置要拦截哪些路径
* 如 addPathPatterns("/**")表示拦截所有请求,包括我们的静态资源
* excludePathPatterns 表示我们要放行哪些(表示不用经过拦截器)
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/login","/")
;
}
}
6 登陆接口和从threadlocal中取数据等测试
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.example.demo_7.util.JwtUtils;
import com.example.demo_7.util.ThreadLocalUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@Data
@AllArgsConstructor
@EqualsAndHashCode
@ToString(exclude = "password")
class User {
private Integer userId;
private String userName;
private String nickName;
private String password;
}
@RestController
public class ControllerTest {
@RequestMapping("/")
public String indexPage() {
return "首页";
}
static HashSet<User> userHashSet = new HashSet<User>();
static {
//模拟数据库
User user1 = new User(1, "zhangsan", "张三", "123456");
userHashSet.add(user1);
User user2 = new User(2, "lisi", "李四", "123123");
userHashSet.add(user2);
userHashSet.add(user2);
}
/**
* 主要是用户密码的校验,并生成jwt字符串返回
* jwt中不要存敏感信息
*
* @param username
* @return
*/
@RequestMapping("/login")
public String login(String username, String password) {
System.out.println("userHashSet = " + userHashSet);
try {
if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) {
return "请输入用户名和密码";
}
User user = userHashSet.stream().filter(uu -> {
return uu.getUserName().equals(username) && uu.getPassword().equals(password);
}).findFirst().get();
CopyOptions copyOptions = CopyOptions.create().setIgnoreProperties("password");
Map<String, Object> map = BeanUtil.beanToMap(user, new HashMap<String, Object>(), copyOptions);
//生成jwt并返回
return JwtUtils.genToken(map);
} catch (Exception e) {
return "用户登陆失败";
}
}
/**
* 演示 从threadlocal中取出信息
*
* @return
*/
@RequestMapping("/threadLocalTest")
public Object threadLocalTest() {
try {
Map<String, Object> map = ThreadLocalUtil.get();
User bean = BeanUtil.toBean(map, User.class);
return "hello:" + bean.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}