JWT全称JSON Web Token,是一个开放标准(RFC7519),用来在各方之间安全地传输信息。JWT可被验证和信任,因为他是数字签名的。
一、JWT的组成
组成 | 作用 | 内容示例 |
Header(头) | 记录令牌类型、签名的算法等 | {"alg":"HS256", "type":"JWT"} |
Payload(有效载荷) | 携带一些用户信息 | {"userid":"1", "username":"admin"} |
Signature(签名) | 防止token被篡改,确保安全性 | 计算出来的签名,一个字符串 |
二、JWT工具类
三、为用户中心和内容中心整合JWT
3.1、在pom.xml中添加依赖
<!--JWT依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.7</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.10.7</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.7</version>
<scope>runtime</scope>
</dependency>
<!-- 添加jedis:Begin -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
3.2、在application.yml中添加属性
spring:
redis:
# redis服务器ip
host: 127.0.0.1
# redis服务器端口号
port: 6379
# redis数据库密码
password:
# redis连接池配置
jedis:
pool:
# 设置连接池中最大连接数
max-active: 10000
# 设置连接池中最大空闲连接数
max-idle: 50
jwt:
secret: aaaaaaabbbbbbcccccdddddaaaaaaabbbbbbcccccdddddaaaaaaabbbbbbcccccddddd
# token有效期,单位秒,默认2周
expire-time-in-second: 1209600
3.3、添加JWT生成类JwtOperator
package com.example.interceptor.utils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.Map;
@Slf4j
@RequiredArgsConstructor
@SuppressWarnings("WeakerAccess")
@Component
public class JwtOperator {
/**
* 秘钥
* - 默认aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt
*/
@Value("${jwt.secret:aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt}")
private String secret;
/**
* token有效期,单位秒
* - 默认30分钟
*/
@Value("${jwt.expire-time-in-second:1209600}")
private Long expirationTimeInSecond;
/**
* 按要求生成token和refresh_token
*
* @param claims
* @return token
*/
public String generateTokens(Map<String, Object> claims){
long currentTime = System.currentTimeMillis();
Date tokenExpirationTime = new Date(currentTime + this.expirationTimeInSecond * 1000);
String token = generateToken(claims, tokenExpirationTime);//生成token
return token;
}
/**
* 判断token是否过期
*
* @param token token
* @return 未过期返回true,否则返回false
*/
public Boolean validateTokenTime(String token) {
Date expiration = getExpirationDateFromToken(token);
return expiration.after(new Date()); //获取时间的毫秒数 > 当前时间的毫秒数 = true
}
/**
* 从token中获取claim
*
* @param token token
* @return claim
*/
public Claims getClaimsFromToken(String token) {
try {
return Jwts.parser()
.setSigningKey(this.secret.getBytes())
.parseClaimsJws(token)
.getBody();
} catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | IllegalArgumentException e) {
log.error("token无效");
throw new IllegalArgumentException("Token invalided.");
}
}
/**
* 生成token
*
* @param claims 用户信息
* @param expirationTime 过期时间
* @return token
*/
private String generateToken(Map<String, Object> claims, Date expirationTime) {
Date createdTime = new Date();
byte[] keyBytes = secret.getBytes();
SecretKey key = Keys.hmacShaKeyFor(keyBytes);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(createdTime)
.setExpiration(expirationTime)
// 你也可以改用你喜欢的算法
// 支持的算法详见:https://github.com/jwtk/jjwt#features
.signWith(key, SignatureAlgorithm.HS256)
.compact();
}
/**
* 获取token的过期时间
*
* @param token token
* @return 过期时间
*/
private Date getExpirationDateFromToken(String token) {
return getClaimsFromToken(token).getExpiration();
}
}
3.4、添加JWT生成类RedisHelper
package com.example.interceptor.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Slf4j
@Component
public class RedisHelper {
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.port}")
private int redisPort;
@Value("${spring.redis.password}")
private String redisPassword;
@Value("${spring.redis.jedis.pool.max-active}")
private String redisMaxActive;
@Value("${spring.redis.jedis.pool.max-idle}")
private String redisMaxIdle;
private JedisPool jedisPool; //连接池
/**
* 向Redis服务器中添加数据
*
* @param key
* @param value
* @return
*/
public String setValue(String key, String value) {
return getJedis().set(key, value);
}
/**
* 根据Key获取Redis中保存的数据
*
* @param key
* @return
*/
public String getValue(String key) {
return getJedis().get(key);
}
/**
* 根据Key删除Redis中保存的数据
*
* @param key
* @return
*/
public Long removeValue(String key) {
return getJedis().del(key);
}
/**
* 获得Jedis对象
*
* @return
*/
private Jedis getJedis() {
// log.info("redisHost:"+redisHost);
// log.info("redisPort:"+redisPort);
// log.info("redisPassword:"+redisPassword);
if(jedisPool == null) {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(Integer.parseInt(redisMaxActive)); //设置最大连接数
jedisPoolConfig.setMaxIdle(Integer.parseInt(redisMaxIdle)); //设置最大空闲连接数
jedisPool = new JedisPool(jedisPoolConfig, redisHost, redisPort); //ip+端口号
}
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
if(!"".equals(redisPassword)){
jedis.auth(redisPassword);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(jedis != null) {
jedis.close(); //关闭jedis连接
}
if(jedisPool != null) {
jedisPool.close(); //关闭jedisPool连接
jedisPool = null;
}
}
return jedis;
}
}
实体类
package com.example.interceptor.entity;
import lombok.Data;
@Data
public class User {
private String id;
private String username;
private String password;
}
service
package com.example.interceptor.service;
import com.example.interceptor.utils.JwtOperator;
import com.example.interceptor.utils.RedisHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
@Slf4j
public class UserService {
@Autowired
private RedisHelper redisHelper;
@Autowired
private JwtOperator jwtOperator;
public String validUser(String username, String password){
String token = null;
if("admin".equals(username) && "123".equals(password)){
String id = "aaaaabbbcc";
//生成token
Map<String,Object> param = new HashMap<>();
param.put("userId",id);
token = jwtOperator.generateTokens(param);
log.info("生成得token是:"+token);
//将token保存到redis中
redisHelper.setValue(id,token);
}
return token;
}
}
TestInterceptor
package com.example.interceptor.interceptor;
import com.example.interceptor.utils.JwtOperator;
import com.example.interceptor.utils.RedisHelper;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Configuration
@Slf4j
public class TestInterceptor extends WebMvcConfigurerAdapter {
@Autowired
private RedisHelper redisHelper;
@Autowired
private JwtOperator jwtOperator;
@Override
public void addInterceptors(InterceptorRegistry registry) {
HandlerInterceptor handlerInterceptor = new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("preHandle请求Controller之前触发,返回值为true代表放行,false代表拦截");
boolean isOk = false;
String servletPath = request.getServletPath();//获取请求得路径
if(servletPath.contains("/user/login")){
isOk = true;//放行
} else {
try {
String token = request.getHeader("Authorization");
boolean isExpire = jwtOperator.validateTokenTime(token);
if(isExpire){//验证token是否被篡改,是否过期
Claims info = jwtOperator.getClaimsFromToken(token);
String[] str = info.toString().replace("{","").replace("}","").split(",");
String userId = str[0].replace("userId=","");
//从Redis中获取token,通过userId
String tokenFromRedis = redisHelper.getValue(userId);
if(tokenFromRedis.equals(token)){
isOk = true;
}
}
} catch (Exception e) {
response.getWriter().write("token is disabled");
}
}
return isOk;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("posHandle请求Controller之后执行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion方法在整个Request请求完成后执行");
}
};
registry.addInterceptor(handlerInterceptor);
}
}
controller
package com.example.interceptor.controller;
import com.example.interceptor.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/login/{username}/{password}")
public String login(@PathVariable String username,@PathVariable String password){
String token = userService.validUser(username, password);
if(token != null){
return "登录成功";
} else {
return "登录失败";
}
}
@GetMapping("/list")
public String[] userList(){
String[] users = {"amdin","tom","mike"};
return users;
}
}