记录SSM+JWT实现用户身份认证
1,导入Maven依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
2,JWTUtils工具类
public class JWTUtil {
public static String createJWT(User user, long ttlMillis, Map<String, Object> claims) throws Exception {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
long nowMillis = System.currentTimeMillis();// 生成JWT的时间
Date now = new Date(nowMillis);
SecretKey key = generalKey();
// 下面就是在为payload添加各种标准声明和私有声明了
JwtBuilder builder = Jwts.builder() // 这里其实就是new一个JwtBuilder,设置jwt的body
.setClaims(claims) // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
.setId(String.valueOf(user.getUserId())) // 这里是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
.setIssuedAt(now) // jwt的签发时间
.setSubject(user.getUserName()) // 这里代表这个JWT的主体,可以是用户名,id,保证唯一就可以了
.signWith(signatureAlgorithm, key);// 设置签名使用的签名算法和签名使用的秘钥
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);
builder.setExpiration(exp); // 设置过期时间
}
return builder.compact(); // 生成jwt
}
/**
* 解密jwt
*/
public static Claims parseJWT(String jwt) throws Exception {
SecretKey key = generalKey(); // 签名秘钥,和生成的签名的秘钥一模一样
Claims claims = Jwts.parser() // 得到DefaultJwtParser
.setSigningKey(key) // 设置签名的秘钥
.parseClaimsJws(jwt).getBody();// 设置需要解析的jwt
return claims;
}
/**
* 由字符串生成加密key
*/
public static SecretKey generalKey() {
byte[] encodedKey = Base64.decodeBase64("123");// 本地的密码解码,这里自定义
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");// 根据给定的字节数组使用AES加密算法构造一个密钥,使用
return key;
}
/**
* 将jwt过期(拉入黑名单)
*
* @param jwt
*/
public static void expireJWT(String jwt) {
Jedis jedis = JedisUtil.getJedis();
if (jedis.exists(jwt)) {
jedis.del(jwt);
}
jedis.set(jwt, jwt, "NX", "PX", 86400000);//默认时间一天
}
/**
* 判断JWT是否在黑名单里
*
* @param jwt
* @return
*/
public static boolean isExpireJWT(String jwt) {
Jedis jedis = JedisUtil.getJedis();
return jedis.exists(jwt);
}
相关的JedisUtil工具类
public final class JedisUtil {
private static JedisPool jedisPool;
static {
//读取配置文件
InputStream is = JedisPool.class.getClassLoader().getResourceAsStream("jedis.properties");
//创建Properties对象
Properties pro = new Properties();
//关联文件
try {
pro.load(is);
} catch (IOException e) {
e.printStackTrace();
jedisPool = null;
}
//获取数据,设置到JedisPoolConfig中
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(Integer.parseInt(pro.getProperty("maxTotal")));
config.setMaxIdle(Integer.parseInt(pro.getProperty("maxIdle")));
//初始化JedisPool
jedisPool = new JedisPool(config, pro.getProperty("host"), Integer.parseInt(pro.getProperty("port")));
}
/**
* 获取连接方法
*/
public static Jedis getJedis() {
if (jedisPool != null)
return jedisPool.getResource();
return null;
}
/**
* 关闭Jedis
*/
public static void close(Jedis jedis) {
if (jedis != null) {
jedis.close();
}
}
3,使用
Map payload = new HashMap();
payload.put("user", user);
JWTUtil.createJWT(user, 86400000, payload);
拉黑示例
String token = JWTUtil.createJWT(user, 86400000, payload);;
JWTUtil.expireJWT(token);
4,实现用户身份认证
4.1,定义认证注解
//用于判断该控制器是否需要用户登录权限
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginAnno {
boolean value() default true;
}
4.2,定义拦截器
4.2.1,配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="cn.interceptor.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
4.2.2,编写控制器代码
public class MyInterceptor implements HandlerInterceptor {
@Autowired
private UserService userService;//userService中有根据用户id获取用户信息的方法,就不拿出来了
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
if (!(object instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) object;
Method method = handlerMethod.getMethod();
if (method.isAnnotationPresent(UserLoginAnno.class)) {
UserLoginAnno userLoginAnno = method.getAnnotation(UserLoginAnno.class);
if (userLoginAnno.value()) {
String token = request.getHeader("token");
// 执行认证
if (token == null) {
throw new RuntimeException("无token,请重新登录!");
}
//检查token是否在黑名单中
if (JWTUtil.isExpireJWT(token)) {
//在黑名单中
throw new RuntimeException("请重新登录");
}
User user;
try {
user = ObjectMapperUtil.readValue(ObjectMapperUtil.writeValueAsString(JWTUtil.parseJWT(token).get("user")), User.class);
} catch (JWTDecodeException j) {
throw new RuntimeException("访问异常!");
}
user = userService.findUserById(user.getUserId());
if (user == null) {
throw new RuntimeException("用户不存在,请重新登录!");
}
request.setAttribute("user", user);//验证过得user对象存入request中,方便使用
return true;
}
}
return true;
}
}
4.3,使用
/**
* 获取用户信息接口
*
* @return
*/
@RequestMapping("findUserInfo")
@UserLoginAnno
@ResponseBody
public Result findUserInfo(HttpServletRequest request) {
return Result.createBySuccess("获取成功", "userInfo", request.getAttribute("user"));
}