任务:
1. 利用JWT生成token,token中包含用户名等信息。实现token反解析用户名功能。
2. 登录时返回token,并且设置登录半小时之后自动退出登录。
3. 配置拦截器,用户工作日志接口需在header中提供Token才能放行。
请利用拦截器和方法注解验证Token是否过期、是否提供。
若未提供,请抛出异常状态。
开发工具:IDEA
技术:Springboot+postman+Jwt
<dependency>
<groupId>com.auth0groupId>
<artifactId>java-jwtartifactId>
<version> 3.10.0version>
dependency>
package com.example.demo.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.stereotype.Repository;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @Author
* @Date 2022/8/1 10:06
* @Description 利用Jwt生成token
*/
@Repository
public class JwtUtils {
private static final String SECRET_KEY = "login";//自定义密钥
private static final long TOKEN_EXPIRE_TIME = 1800000L ;//设置过期时间30min
/**
* 生成 jwt token
*/
public static String creatJwtTo(int id,String userId,String username){
Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY);//SECRET_KEY自定义密钥
// 设置头信息
HashMap<String, Object> header = new HashMap<>(2);
header.put("typ","JWT");
header.put("alg","HS256");
// TokenVo tokenVo = new TokenVo();
// 生成 Token
String token = JWT.create().withHeader(header)
.withClaim("id", id)//自定义参数
.withClaim("userId", userId)//自定义参数
.withClaim("userName",username)//自定义参数
.withExpiresAt(new Date(System.currentTimeMillis() + TOKEN_EXPIRE_TIME)).sign(algorithm);//TOKEN_EXPIRE_TIME token过期时间 毫秒
return token;
}
/**
* 解析 jwt
*/
public static Map<String, Claim> parseJwtTo(String token){
Map<String, Claim> claims = null;
try {
Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY);//SECRET_KEY自定义密钥
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token);//需要解析token
claims = jwt.getClaims();
return claims;
} catch (Exception e) {
return null;
}
}
/**@RequestHeader("token")
* 验证token合法性
*/
public static void verify(String token) throws Exception{
try {
DecodedJWT verify = JWT.require(Algorithm.HMAC256(SECRET_KEY )).build().verify(token);
System.out.println(verify);
int id=verify.getClaim("id").asInt();
String userId = verify.getClaim("userId").asString();
String username = verify.getClaim("userName").asString();
System.out.println(id);
System.out.println(userId);
System.out.println(username);
}catch (TokenExpiredException e) {
throw new TokenExpiredException("令牌过期");
} catch (JWTVerificationException e) {
throw new RuntimeException("令牌不正确");
}catch (NullPointerException e){
throw new NullPointerException("缺少token");
}
}
}
//登录
@PostMapping(value = "/login")
public Map<String,Object> login(@RequestBody Map<String, String> person) throws ServiceException{
String username=person.get("username");
String password=person.get("password");
//1. 判断用户名、密码是否为空
if (username != null && password != null) {
List<User> users = service.queryByUserName(username);
//2. 判断用户名是否存在
if (users != null && users.size() > 0) {
User user = users.get(0);
//3. 判断密码是否正确
RedisUtils red=new RedisUtils();
int i= red.checkUser(user,password);
if(i==0){
int id=user.getId();
String userId=user.getUserId();
String token= JwtUtils.creatJwtTo(id,userId,username);
Map<String,Object> data=new HashMap<>();
data.put("token",token);
return data;
}else if(i==1){
throw new ServiceException(500,"密码错误!登录失败");
}else{
throw new ServiceException(500,"登录异常,账户锁定,请等待系统解锁");
}
} else {
throw new ServiceException(500,"登陆失败,用户名不存在");
}
} else {
throw new ServiceException(500,"登陆失败,请检查用户名、密码是否为空");
}
}
}
package com.example.demo.common;
import cn.hutool.jwt.Claims;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.example.demo.config.MyContext;
import com.example.demo.utils.JwtUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* @Author
* @Date 2022/8/1 14:26
* @Description 拦截器
*/
//认证拦截器
@Component //添加到容器
public class AuthenticationFilter implements HandlerInterceptor {
@Autowired
private JwtUtils jwtUtils;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String login = request.getRequestURI();
if (login.contains("/login")) {
return true;
}
// //获取请求头中的token
final String token;
final String authHeader = request.getHeader("Authorization");
if (StringUtils.isNotBlank(authHeader) && authHeader.startsWith("Bearer ")) {
// 截取token
token = authHeader.substring(7);
} else {
if (request.getHeader("token") != null) {
token = request.getHeader("token");
} else {
token = request.getParameter("token");
}
}
Map<String, Object> mp = new HashMap<>();
try {
if (token == null) {
System.out.println("token空");
}
jwtUtils.verify(token);//验证token
return true; //上一句无异常就 放行
} catch (SignatureVerificationException e) {
// e.printStackTrace();
mp.put("msg", "无效签名");
} catch (TokenExpiredException e) {
e.printStackTrace();
mp.put("msg", "token过期,请重新登录");
} catch (AlgorithmMismatchException e) {
// e.printStackTrace();
mp.put("msg", "算法不匹配");
} catch (NullPointerException e) {
// e.printStackTrace();
mp.put("msg", "token不能为空");
} catch(RuntimeException e){
e.printStackTrace();
mp.put("msg", "token不正确");
} catch (Exception e) {
// e.printStackTrace();
mp.put("msg", "其他异常");
}
mp.put("state", false);
//map转换为json
String json = new ObjectMapper().writeValueAsString(mp);
response.setContentType("application/json; charset=UTF-8");
//返回
response.getWriter().println(json);
return false;
}
}
package com.example.demo.config;
import com.example.demo.common.AuthenticationFilter;
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.WebMvcConfigurationSupport;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Author
* @Date 2022/8/1 14:44
* @Description
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired//必须有这个注解
private AuthenticationFilter authenticationFilter;
// 注入拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationFilter)
// 拦截所有请求
// .addPathPatterns("/**")
//拦截工作计划接口
.addPathPatterns("/workPlan/**")
// 排除登录请求
.excludePathPatterns("/user/login");
}
}
token令牌结构:header.payload.signature
利用JWT生成、解析、验证token
拦截器配置:①认证拦截器 ②注册拦截器
//拦截接口 .addPathPatterns(“接口”)
//排除接口 .excludePathPatterns(“接口”)
postman设置token方法
way1:加在请求头部
way2:使之成为全局
输出上述代码后,执行(send)
查看:
TokenExpiredException、ExpiredJwtException、RuntimeException
报错:无法自动装配,找不到’JwtUtils’类型的Bean
通过添加注解@Repository
拦截器、过滤器
借鉴的文章部分链接如下:
https://blog.csdn.net/qq_26139541/article/details/110385619
https://www.cnblogs.com/txt1024/p/15730756.html