目录
完善用户信息
完善用户信息
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 {
@Resource
private 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中的用户id
Long 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<>();
//将用户对象,存入Threadlocal
public static void set(User user) {
tl.set(user);
}
//从当前线程,获取用户对象
public static User get() {
return tl.get();
}
//从当前线程,获取用户对象的id
public 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;
// 加密KEY
private 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 {
@Override
public 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对象 存入ThreadLocal
Claims 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 {
@Override
public 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);
}
}
用户信息完善和更新头像上传成功,则进入系统首页。