目录
用户信息部分
1、获取用户详细信息
前言
代码分析
代码实现
测试
2、更新用户信息
前言
代码实现
测试
3、更新用户头像
前言
代码实现
测试
4、更新用户密码
前言
代码实现
测试
用户信息部分
1、获取用户详细信息
前言
承接上一篇博客登录注册功能实现
由于我们的数据库字段是下划线命名的creat_time,而实体类中对应的是creatTime,字段不一致无法直接获取。
因此需要在yml配置文件中开启驼峰命名
mybatis:configuration:mapUnderscoreToCamelCase: true
既然要获取用户的详细信息,所以会自然的想到在controller中写一个/info接口,然后在service中调用mapper,mapper再调数据库,这样也没有错。但是我们再学习token的时候就说过了,token本身就是由用户的各种信息组成的,所以其实我们直接从token获取用户信息就行了,这样效率更高代码更少。
代码分析
首先由于token中包含了用户的各种信息,所以默认也会包含密码在内,但是我们不希望在响应的时候把密码也解析出来,这是很不安全的,因此我们需要使用一个注解@JsonIgnore
@JsonIgnore:让springmvc把当前对象转换为json字符串的时候,忽略掉该注解作用的字段,最终的json中就没有这个字段了
其次我们在创建拦截器的时候就对令牌进行了一次解析响应,虽然我们在使用的时候也可以重新写一遍解析jwt,但是既然已经被解析过了,我们还是直接去拦截器中取比较好,这样显得比较专业和逼格。为此我们需要使用到ThreadLocal
ThreadLocal:提供线程变量
- 用来存储数据:get()和set()
- 使用ThreadLocal存储的数据,线程安全
线程安全就是说假如我们直接使用一个全局静态变量才存储变量,那么两个不同的线程同时会对这一个变量进行操作,数据就会错乱。但是使用ThreadLocal就可以保证不同的线程中只使用到自己的变量,不会互相串数据
代码实现
ThreadLocalUtil工具类,这个类是为了让我们更方便的使用ThreadLocal。其中我们声明了一个全局的静态变量THREAD_LOCAL,可以对其进行set值和get值,还有一个remove方法是因为它的生命周期很长,为了防止内存泄漏要在一次线程完成时把这个变量的值删掉
/*** ThreadLocal 工具类*/
@SuppressWarnings("all")
public class ThreadLocalUtil {//提供ThreadLocal对象,private static final ThreadLocal THREAD_LOCAL = new ThreadLocal();//根据键获取值public static <T> T get(){return (T) THREAD_LOCAL.get();}//存储键值对public static void set(Object value){THREAD_LOCAL.set(value);}//清除ThreadLocal 防止内存泄漏public static void remove(){THREAD_LOCAL.remove();}
}
优化拦截器 ,相比上次写的多了一个afterCompletion方法,这个方法也是顾名思义在目标方法执行后它再执行
@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取令牌String token = request.getHeader("Authorization");//验证tokentry {Map<String,Object> claims = JwtUtil.parseToken(token);//将解析的token信息放入ThreadLocalThreadLocalUtil.set(claims);//没有异常就放行return true;} catch (Exception e) {//未登录,不放行response.setStatus(401);return false;}}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//清空ThreadLocal的数据ThreadLocalUtil.remove();}
}
Controller
可以直接这里面获取ThreadLocal的值,之前往里存放的是什么类型的值就用什么类型接收就行了。
@RestController
@RequestMapping("/article")
public class ArticleController {@Autowiredprivate UserService userService;@GetMapping("/list")public Result<User> list(){Map<String,Object> map = ThreadLocalUtil.get();String username = (String) map.get("username");User user = userService.findByUserName(username);return Result.success(user);}
}
测试
发起请求
2、更新用户信息
前言
使用者在前端修改用户的个人信息,发送到后端。我们接收过来的就是一个User对象,但是spring无法直接识别一个对象类,所以我们需要使用注解@RequestBody。
其次就是参数校验,实际上这个功能是要在前端完成的,不过我们在后端也写一下吧。我们之前在注册模块对密码进行过检验,让他限定在5~16位。使用Spring Validation,在需要被校验的参数前加上@Pattern(regexp = "^正则表达式$"),在需要生效的地方加上@Validated就完成了。
代码实现
user实体类,直接在类上加上注解即可参数校验
测试
数据校验
3、更新用户头像
前言
因为头像只是更新用户信息中的一部分,所以使用PATCH请求方式
avaterUrl是头像的地址,一般都存放在阿里云中
代码实现
controller
@URL是数据校验是否是一个url格式的字符串,防止胡乱上传
@PatchMapping("/updateAvatar")public Result updateAvatar(@RequestParam @URL String avatarUrl){userService.updateAvatar(avatarUrl);return Result.success(avatarUrl);}
service
直接从mapper中获取当前登录人token中的id
@Overridepublic void updateAvatar(String avatarUrl) {Map<String,Object> map = ThreadLocalUtil.get();Integer id = (Integer) map.get("id");userMapper.updateAvatar(avatarUrl,id);}
mapper
@Update("update user set user_pic=#{avatarUrl},update_time=now() " +"where id = #{id}")void updateAvatar(String avatarUrl,Integer id);
测试
4、更新用户密码
前言
同样地,更新密码也是用户信息整体的一部分,所以用PATCH请求方式
在之前我们更新用户基本信息的时候,传递的参数是一个user实体类参数,且json字段名和user属性名一摸一样。但是在更新密码中,有一个new_pwd和old_pwd来表示新密码和旧密码,user类中就没有这个属性了,添加进去在逻辑上也不太合适。所以我们可以通过传递一个map集合来表示这个参数。
由于我们的密码在数据库中已经经过了MD5加密,所以直接拿传递过来的参数与数据库中的密码比对是不行的,可以先对参数进行MD5加密,然后在与数据库数据进行比对检验是否正确。
代码实现
Controller
@PatchMapping("/updatePwd")public Result updatePwd(@RequestBody Map<String,String> params){//校验参数String oldPwd = params.get("old_pwd");String newPwd = params.get("new_pwd");String rePwd = params.get("re_pwd");if (!StringUtils.hasLength(oldPwd) || !StringUtils.hasLength(newPwd)|| !StringUtils.hasLength(rePwd)){return Result.error("缺少必要的参数");}//比较原密码是否正确Map<String,Object> map = ThreadLocalUtil.get();String username = (String) map.get("username");User loginUser = userService.findByUserName(username);if (!loginUser.getPassword().equals(Md5Util.getMD5String(oldPwd))){return Result.error("原密码错误");}//比较新密码和重复新密码是否正确if (!rePwd.equals(newPwd)){return Result.error("两次填写的新密码不一样");}//调用service完成密码更新userService.updatePwd(newPwd);return Result.success();}
Service
@Overridepublic void updatePwd(String newPwd) {Map<String,Object> map = ThreadLocalUtil.get();Integer id = (Integer) map.get("id");userMapper.updatePwd(Md5Util.getMD5String(newPwd),id);}
Mapper
@Update("update user set password=#{newPwd},update_time=now() where id = #{id} ")void updatePwd(String newPwd,Integer id);