> 文章列表 > 【新时代圈友app】解析token的两种方法:工具类/拦截器—人脸识别设置头像、完善用户信息

【新时代圈友app】解析token的两种方法:工具类/拦截器—人脸识别设置头像、完善用户信息

【新时代圈友app】解析token的两种方法:工具类/拦截器—人脸识别设置头像、完善用户信息

目录

完善用户信息

1.请求参数

解析token的两种方法:工具类/拦截器

2.解析token

3.令牌拦截器

工具类:UserHeader

为什么要使用ThreadLocal?

令牌拦截器:TokenInterceptor

注册拦截器:WebConfig

4.设置更新头像


完善用户信息

1.请求参数

        用户完善信息,选择输入性别、昵称、出生日期、地址等信息将其封装成UserInfo类通过@RequestBody注解将前端传递的json数据中key和value值进行封装为指定类;即@RequestBody注解后面所表示的类,或者是其他数据数据类型。

同时还需要从请求头中获取登录时颁发的token令牌,通过@RequestHeader()用于将请求的头信息数据映射到功能处理方法的参数上;并指定字符串内容为Authorization以此来获取请求头中保存的token来进行身份验证。

YApi方法请求参数规范

 UserController

接口路径:/user/loginReginfo

import com.tanhua.model.domain.UserInfo;
import com.tanhua.server.interceptor.UserHeader;
import com.tanhua.server.service.UserInfoService;
import com.tanhua.server.service.UserService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import java.io.IOException;
import java.util.Map;@RestController
@RequestMapping("/user")
public class UserController {@Resourceprivate UserInfoService userInfoService;   /*** 完善用户信息* @param userInfo* @param token* @return*/@PostMapping("/loginReginfo")public ResponseEntity loginReginfo(@RequestBody UserInfo userInfo, @RequestHeader("Authorization")  String token){// @RequestHeader("Authorization")  获取请求头中Authorization中的token//1.解析token
//        Claims claims = JwtUtils.getClaims(token);
//        Integer id = (Integer) claims.get("id");//2、向userinfo中设置用户id
//        userInfo.setId(Long.valueOf(id));//以上是解析token的方法一,下面是通过拦截器解析获取token信息的方法二,二选一//请求时,请求拦截器拦截获取token信息//获取token中的用户idLong id = UserHeader.getUserId();userInfo.setId(id);//保存userInfoService.save(userInfo);return ResponseEntity.ok(null);}
}

解析token的两种方法:工具类/拦截器

2.解析token

        方法一:通过JwtUtils工具类中的getClaims()方法传入token,返回一个JSON映射的Claims实例对像。通过Claims对象的get()方法传入id获取登录用户的id值并返回。将id进行保存,后端调用通用mapper的insert()方法进行保存。

具体请参上方代码:即标题1.请求参数中的代码块

在这里,由于在后面的一系列操作中几乎每次都会使用到,每次请求controller方法时都需要在方法中写一次,未免有点麻烦,因此在这里写了一个令牌拦截器TokenInterceptor

3.令牌拦截器

方法二:令牌拦截器,在每次controller请求前都进行拦截,并获取请求头token的信息,定义工具类UserHeader,实现向ThreadLocal存储数据的方法。并可以通过调用该类的方法来获取token的用户数据信息。

工具类:UserHeader

 创建一个私有静态字段ThreadLocal<User>线程本地变量,这里需指定一个User实体对象,希望将该状态和线程进行关联。每一次访问通过get或set方法来获取设置当前线程的信息。

这里我们定义了4个方法,分别是:

        get()设置为从当前线程获取用户对象;

        set()将用户对象存入ThereadLocal中;

        getUserId()从当前线程,获取用户对象的id,通过调用get()方法获取当前用户对象,并获取id进行返回;

        getMobile()从当前线程,获取用户对象的手机号码,同样通过调用get()方法获取当前用户对象,并获取id进行返回;

/*** 工具类:实现向ThreadLocal存储数据的方法*/
public class UserHeader {//创建一个ThreadLocal 线程本地变量private static ThreadLocal<User> tl = new ThreadLocal<>();//将用户对象,存入Threadlocalpublic static void set(User user) {tl.set(user);}//从当前线程,获取用户对象public static User get() {return tl.get();}//从当前线程,获取用户对象的idpublic static Long getUserId() {return tl.get().getId();}//从当前线程,获取用户对象的手机号码public static String getMobile() {return tl.get().getMobile();}
}

为什么要使用ThreadLocal?

        项目要存在用户信息,由于这种关键信息不适合传参的方式,前端将用户信息封装到header里,后台通过拦截器获取,考虑项目没有使用多线程的情况,就用户信息存储在ThreadLocal里,方便拿去,也可以通过全局变量的方式。


令牌拦截器:TokenInterceptor

        该类实现HandlerInterceptor接口,该接口是SpringMVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。重写覆盖该接口的preHandle()方法,表示在请求前执行该方法。

        在该方法中获取请求头中的token并对其信息进行保存封装到UserHead类中。通过request.getHeader()方法传入参数Authorization来获取请求头token信息。通过调用JwtUtils工具类的方法verifyToken()传入token字符串判断该令牌是否有效。通过Jwts工厂类的.parser()解析器解析JWT字符串,通过.setSigningKey()设置签名密钥,.parseClaimsJwt()传入token字符串,声明要解析的Jws,.getBody()返回JWT报头,如果不存在则返回null。对此过程进行try-catch如果没有出现异常则返回true,反之返回false。

public class JwtUtils {// TOKEN的有效期1小时(S)private static final int TOKEN_TIME_OUT = 3_600;// 加密KEYprivate static final String TOKEN_SECRET = "itcast";/*** 是否有效 true-有效,false-失效*/public static boolean verifyToken(String token) {if(StringUtils.isEmpty(token)) {return false;}try {Claims claims = Jwts.parser()   //解析器.setSigningKey("itcast")    //设置签名密钥.parseClaimsJws(token)      //解析声明Jws.getBody();       //返回JWT报头}catch (Exception e) {return false;}return true;}
}

        对返回的布尔变量进行判断,如果token失效则返回状态401拦截,并返回false;如果token正常可用则放行。

        通过JwtUtils工具类的getClaims(token)方法解析token,返回一个JSON映射Claims类,调用该类的get()方法获取id和手机号码,并构造User对象进行保存。在通过UserHead类的set(User)方法,传入User对象,用于将该对象存入Threadlocal中,并返回true.

mport com.tanhua.commons.utils.JwtUtils;
import com.tanhua.model.domain.User;
import io.jsonwebtoken.Claims;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class TokenInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1、获取请求头String token = request.getHeader("Authorization");//2、适用工具类 判断token是否有效boolean verifyToken = JwtUtils.verifyToken(token);//3、如果token失效 返回状态401 拦截if(!verifyToken){response.setStatus(401);return false;}//4、如果token正常可用 发行//解析token 获取id和手机号码 构造User对象 存入ThreadLocalClaims claims = JwtUtils.getClaims(token);String mobile = (String) claims.get("mobile");Integer id = (Integer) claims.get("id");User user = new User();user.setId(Long.valueOf(id));user.setMobile(mobile);UserHeader.set(user);return true;}
}

注册拦截器:WebConfig

该类实现Web Mvc配置器接口WebMvcConfigurer,重写添加拦截器的方法addInterceptors(InterceptorRegistry registry),将我们编写的令牌拦截器进行注册添加到WebMvc的配置中。通过registry.addInterceptor()传入令牌拦截器类进行添加注册;.addPathPatterns("/**")设置需要拦截的路径,以及通过.excludePathPatterns()来设置不需要拦截的路径,一般进行登录校验的时候不需要进行拦截,这里可以进行设置。

注意:该类必须使用@Configuration注解,将该类标识为SpringBoot的配置类。

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 {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new TokenInterceptor()).addPathPatterns("/**").excludePathPatterns(new String[]{"/user/login","/user/loginVerification"});}
}

4.设置更新头像

YApi方法请求参数规范

        需接收前端上传的一个文件图片,通过MultiparFile做为方法参数来实现文件的上传功能。我们在使用MultipartFile作为参数传递的时候,可以将MultipartFile声明为一个数组,这样就能支持多文件传输,如果只需要传输一个文件,则去掉数组就好了。

        并通过请求拦截器获取token信息,使用UserHeader.getUserId()的方法来获取用户的id;用户id和文件参数作为更新用户的方法一起传入。

上传图片通过调用OSS阿里云的操作系统模板类OssTemplate将图片上传到阿里云,调用该类的upload()上传方法,getOriginalFilename()获取原始文件名和getInputStream()输入流,进行上传并返回图像路径字符串imageUrl

识别校验图片:通过调用百度云人脸识别,判断图片是否包含人脸。通过aip面模板aipFaceTemplate类的detect()检测图片路径,传入上传到OSS阿里云图片返回的图像路径,返回一个布尔值。对其值进行判断,如果不包含人脸则抛出异常;反之包含人脸,创建用户对象,设置id和图片路径,调用用户Api通用mapper方法updateById()传入用户对象通过用户id对其信息进行更新。

  //更新用户头像public void updateHead(MultipartFile headPhoto,Long id) throws IOException {//将图片上传到OSS阿里云String imageUrl = ossTemplate.upload(headPhoto.getOriginalFilename(), headPhoto.getInputStream());//2.调用百度云判断是否包含人脸boolean detect = aipFaceTemplate.detect(imageUrl);//如果不包含人脸,抛出异常if (!detect){throw new RuntimeException();}else {//包含人脸,调用API更新UserInfo userInfo = new UserInfo();userInfo.setId(id);userInfo.setAvatar(imageUrl);userInfoApi.update(userInfo);}}

用户信息完善和更新头像上传成功,则进入系统首页。