springboot后台开发记录————token的使用
一、token的作用
session和token都是用来保持会话,功能相同;
而在前后端分离的项目中,使用token能够降低服务器压力,登录成功后,服务端会在响应主体中将{token:‘字符串’}返回给客户端。客户端通过cookie、sessionStorage、localStorage都可以进行存储。再次请求时不会默认携带,需要在请求拦截器位置给请求头中添加认证字段Authorization携带token信息,服务器端就可以通过token信息查找用户登录状态。
二、设置拦截器
在springboot中设置要拦截的路径,除了登录以外,其他的请求全部都要在请求头里携带token
1、拦截请求
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(new ReqInterceptor()).addPathPatterns("/**")
.excludePathPatterns("/auth/login");
}
}
拦截除登录以外的路径
2、自定义Token工具类
token实际上是对信息进行加密后的一串字符串,token里面一般包含的信息有用户相关信息,创建时间,存活时间等,这些信息在解码token时能够判断token属于谁以及是否过期等
先引入依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
TokenUtil.java
里面封装了两种创建token的方法和解析token的方法
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TokenUtil {
/****
* 默认为10天的token
* @param uid
* @param openid
* @return
*/
public static String createToken(String uid,String openid){
String token;
token= JWT.create().withClaim("uid",uid)
.withClaim("generatetime",System.currentTimeMillis())
.withClaim("exptime",1000*1*60*60*24*10)//设置token过期时间为10天
.sign(Algorithm.HMAC256(openid));
return token;
}
/****
* 自定义有效时长的token
* @param uid
* @param account
* @param liveTime
* @return
*/
public static String createToken(String uid,String account,Long liveTime){
String token;
token= JWT.create().withClaim("uid",uid)
.withClaim("generatetime",System.currentTimeMillis())
.withClaim("exptime",liveTime)//设置token过期时间为10天
.sign(Algorithm.HMAC256(account));
return token;
}
/****
* 返回参数
* true:toke有效
* false:token无效
* @param token
* @return
*/
public static boolean decodeToken(String token){
Long currentTime = System.currentTimeMillis();
try {
Long generateTime = JWT.decode(token).getClaim("generatetime").asLong();
Long expTime = JWT.decode(token).getClaim("exptime").asLong();
if(currentTime - generateTime > expTime){
return false;
}
else return true;
}catch (Exception e){
return true;
}
}
}
3、处理被拦截的请求
逻辑是将被拦截的请求中的头文件中的token取出来,进行解析,如果为空或者解析失败或者过期,则token为无效的token,访问将被拒绝,需要重新登录以获取token
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.interfaces.DecodedJWT;
import com.program.facesigninsystem.entity.ResponseModel;
import net.sf.json.JSONObject;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Base64;
public class ReqInterceptor extends HandlerInterceptorAdapter {
/**
* 一个拦截器
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//得到请求头
String Authorization = request.getHeader("Authorization");//获得token
String token = Authorization.substring(7);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
//如果token没有则返回
if(token==null || token.length()==0){
JSONObject jsonObject = JSONObject.fromObject(new ResponseModel(310, "无token,不允许访问!").toString());
response.getWriter().append(jsonObject.toString()).flush();
return false;
}
else {
try{
Long currentTime = System.currentTimeMillis();
Long generateTime = JWT.decode(token).getClaim("generatetime").asLong();
Long expTime = JWT.decode(token).getClaim("exptime").asLong();
if(currentTime - generateTime > expTime) {
JSONObject jsonObject = JSONObject.fromObject(new ResponseModel(310, "token过期,请重新登录!").toString());
response.getWriter().append(jsonObject.toString()).flush();
return false;
}
}catch (Exception e){
e.printStackTrace();
//验证失败
JSONObject jsonObject = JSONObject.fromObject(new ResponseModel(310, "token无效,不允许访问!").toString());
response.getWriter().append(jsonObject.toString()).flush();
return false;
}
}
return super.preHandle(request, response, handler);
}
}
三、token的颁发
import com.program.facesigninsystem.entity.ResponseModel;
import com.program.facesigninsystem.entity.UserInfo;
import com.program.facesigninsystem.service.IUserInfoService;
import com.program.facesigninsystem.util.HttpClient;
import com.program.facesigninsystem.util.TokenUtil;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Random;
@CrossOrigin
@RestController
@RequestMapping(value = "/auth")
public class UserController {
final private String APP_ID = "******************";
final private String SECRET = "*****************************";
@Autowired
IUserInfoService iUserInfoService;
@RequestMapping(value = "/login",method = RequestMethod.POST)
private ResponseModel loginUser(@RequestBody JSONObject account){
ResponseModel responseModel;
String openid = "";
try{
openid = HttpClient.getWxUserOpenid(account.get("code").toString(),APP_ID,SECRET).get("openid").toString();
}catch (Exception e){
responseModel = new ResponseModel(199,"微信授权失败");
return responseModel;
}
UserInfo userInfo = iUserInfoService.loginUser(openid);
if(userInfo!=null) {
//判断该用户token是否为空
if(userInfo.getToken()==null||userInfo.getToken().equals("")){
String token = TokenUtil.createToken(userInfo.getUid(),userInfo.getOpenid(),1000*60l);
userInfo.setToken(token);
iUserInfoService.updateToken(userInfo.getUid(),token);//token存入用户数据库
}
else{
if(!TokenUtil.decodeToken(userInfo.token)){
String token = TokenUtil.createToken(userInfo.getUid(),userInfo.getOpenid());
userInfo.setToken(token);
iUserInfoService.updateToken(userInfo.getUid(),token);//token存入用户数据库
}
}
responseModel = new ResponseModel(200,"登录成功",userInfo);
}
else{
String uid = "";
Random random = new Random();
for (int i = 0; i < 20; i++) {
uid += String.valueOf(random.nextInt(10));
}
userInfo = new UserInfo();
String token = TokenUtil.createToken(uid,openid);
userInfo.setToken(token);
userInfo.setUid(uid);
userInfo.setOpenid(openid);
iUserInfoService.addUser(uid,openid,token);
responseModel = new ResponseModel(200,"登录成功",userInfo);
}
return responseModel;
}
}
这个接口是对微信登录的验证,着重来看逻辑的处理:
用户登录发起登录请求时,判断数据库中是否存在该用户:
1、如果不存在,则创建该用户,将用户相关信息以及token创建时间和存活时间存于token中,并为其颁发token,同时存于数据库中,并将token和相关信息返回;
2、如果存在该用户,则对该用户的token进行解析:
(1)如果token已经过期,则颁发新的token,同时存于数据库,并将token和相关信息返回
(2)如果token有效,则直接将token和相关信息返回