序、说明
这个是根据上一篇【Springboot&Mybatis&Mysql】基础上的拓展,主要是实现token鉴权的后端部分。
一、前情回顾
先看下上篇中的项目结构。
很简单,就实现了controller和数据部分
二、创建Token
1、添加依赖:jsonwebtoken
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
2、在Utils中创建token工具类:TokenManager
package com.javaexample.springboot.Utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
public class TokenManager {
public static final String SECRET = "admin";//密钥,自己设
public static String createJwtToken(int userId, String userName) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", userId);
claims.put("userName", userName);
long ttlMillis = 10800000; //默认1小时
return createJwtToken(claims, ttlMillis);
}
public static String createJwtToken(int userId, String userName, long ttlMillis) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", userId);
claims.put("userName", userName);
return createJwtToken(claims, ttlMillis);
}
public static String createJwtToken(Map<String, Object> claims, long ttlMillis) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
int jwt_id = (int) (1 + Math.random() * (1000000));
JwtBuilder builder = Jwts.builder().setClaims(claims)
.setId(String.valueOf(jwt_id))
.setIssuedAt(now)
.signWith(signatureAlgorithm, signingKey);
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);
builder.setExpiration(exp);
}
return builder.compact();
}
public static Claims parseJWT(String jwt) {
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(SECRET))
.parseClaimsJws(jwt).getBody();
return claims;
}
}
3、在Model中添加实体类:TokenPojo
package com.javaexample.springboot.Model;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
import java.io.Serializable;
//设置跟着session来,多用户登录时不会搞错token
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component(value = "TokenPojo")
public class TokenPojo implements Serializable {
private static final long serialVersionUID = 9134726889277808512L;
private int userId;
private String userName;
//getter & setter & toString
}
4、调整UserServiceImpl中的userLogin
public ResponsePojo userLogin(String userName,String password){
UserEntity user = userMapper.userLogin(userName, password);
if (user == null) {
return responseManager.buildError(11002, "密码错误");
}
userMapper.updateLoginInfo(user);
String accessToken = tokenManager.createJwtToken(user.getUserId(), user.getUserName());
Map<String, String> data = new Hashtable<>();
data.put("accessToken", accessToken);
return responseManager.buildSuccess(data);
}
5、试运行
重新启动项目,并调用登陆接口,可以看到返回根据userId和userName生成的token
之后在前端保存token就行了,如果后端需要保存token,那么在更新登录时间时同步更新token即可。
三、拦截cookie,捕获token
我的前端主要是把token保存在cookie中,传递给后端时拦截cookie并对token进行解析,然后就可以为所欲为了
1、创建拦截器CookieInterceptor
先创建Interceptor文件夹,和Controller这些同级
并在这个目录下CookieInterceptor
package com.javaexample.springboot.Interceptor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.javaexample.springboot.Model.ResponsePojo;
import com.javaexample.springboot.Model.TokenPojo;
import com.javaexample.springboot.Utils.ResponseManager;
import com.javaexample.springboot.Utils.TokenManager;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CookieInterceptor extends HandlerInterceptorAdapter {
@Autowired
private ResponseManager responseManager;
@Autowired
private TokenManager tokenManager;
@Autowired
private TokenPojo tokenPojo;
@Autowired
private ObjectMapper mapper;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("access_token")) {
String cookieStr = cookie.getValue();
try {
Claims claims = tokenManager.parseJWT(cookieStr);
tokenPojo.setUserId((Integer) claims.get("userId"));
tokenPojo.setUserName((String) claims.get("userName"));
return true;
}catch (Exception ex){
System.out.println(ex);
ResponsePojo responsePojo = responseManager.buildError(10001,"token无效");
response.getWriter().write(mapper.writeValueAsString(responsePojo));
return false;
}
}
}
System.out.println("no access_token");
ResponsePojo responsePojo = responseManager.buildError(10002,"no token");
response.getWriter().write(mapper.writeValueAsString(responsePojo));
return false;
}else{
System.out.println("no cookie");
ResponsePojo responsePojo = responseManager.buildError(10003,"no cookie");
response.getWriter().write(mapper.writeValueAsString(responsePojo));
return false;
}
}
}
2、配置WebConfig
在Config文件夹下创建WebConfig
package com.javaexample.springboot.Config;
import com.javaexample.springboot.Interceptor.CookieInterceptor;
import org.springframework.context.annotation.Bean;
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 {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(CookieInterceptor())
.addPathPatterns("/**") //添加需要拦截的url
.excludePathPatterns("/user/login"); //添加不需要拦截的url,白名单
}
@Bean
public CookieInterceptor CookieInterceptor() {
return new CookieInterceptor();
}
}
四、试运行
1)先不带token调用获取userinfo的接口
被拦截并提示没有token,前端收到错误后进行处理即可
2)带token调用获取userinfo接口
五、token的使用
后端拦截到token后,在调用接口时可以根据token获取里面的信息,如调用获取userinfo时查看是谁调用的。
对UserServiceImpl中的getUserInfo方法进行调整
public ResponsePojo getUserInfo(int userId) {
UserEntity user = userMapper.getUserInfo(userId);
System.out.println("token's user name is: "+tokenPojo.getUserName());
return responseManager.buildSuccess(user);
}