做一个springboot用户信息模块

目录

用户信息部分

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中就没有这个字段了

做一个springboot用户信息模块_第1张图片

        其次我们在创建拦截器的时候就对令牌进行了一次解析响应,虽然我们在使用的时候也可以重新写一遍解析jwt,但是既然已经被解析过了,我们还是直接去拦截器中取比较好,这样显得比较专业和逼格。为此我们需要使用到ThreadLocal

ThreadLocal:提供线程变量

  • 用来存储数据:get()和set()
  • 使用ThreadLocal存储的数据,线程安全

        线程安全就是说假如我们直接使用一个全局静态变量才存储变量,那么两个不同的线程同时会对这一个变量进行操作,数据就会错乱。但是使用ThreadLocal就可以保证不同的线程中只使用到自己的变量,不会互相串数据        

做一个springboot用户信息模块_第2张图片

代码实现 

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 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 {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取令牌
        String token = request.getHeader("Authorization");
        //验证token
        try {
            Map claims = JwtUtil.parseToken(token);

            //将解析的token信息放入ThreadLocal
            ThreadLocalUtil.set(claims);

            //没有异常就放行
            return true;
        } catch (Exception e) {
            //未登录,不放行
            response.setStatus(401);
            return false;
        }

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        
        //清空ThreadLocal的数据
        ThreadLocalUtil.remove();
    }
}

 Controller

        可以直接这里面获取ThreadLocal的值,之前往里存放的是什么类型的值就用什么类型接收就行了。

@RestController
@RequestMapping("/article")
public class ArticleController {

    @Autowired
    private UserService userService;

    @GetMapping("/list")
    public Result list(){

        Map map = ThreadLocalUtil.get();
        String username = (String) map.get("username");
        User user = userService.findByUserName(username);

        return Result.success(user);
    }
}

测试

发起请求

做一个springboot用户信息模块_第3张图片

 2、更新用户信息

前言

        使用者在前端修改用户的个人信息,发送到后端。我们接收过来的就是一个User对象,但是spring无法直接识别一个对象类,所以我们需要使用注解@RequestBody。

        其次就是参数校验,实际上这个功能是要在前端完成的,不过我们在后端也写一下吧。我们之前在注册模块对密码进行过检验,让他限定在5~16位。使用Spring Validation,在需要被校验的参数前加上@Pattern(regexp = "^正则表达式$"),在需要生效的地方加上@Validated就完成了。

做一个springboot用户信息模块_第4张图片

代码实现

user实体类,直接在类上加上注解即可参数校验

做一个springboot用户信息模块_第5张图片

测试

数据校验

做一个springboot用户信息模块_第6张图片

做一个springboot用户信息模块_第7张图片

3、更新用户头像

前言

因为头像只是更新用户信息中的一部分,所以使用PATCH请求方式

做一个springboot用户信息模块_第8张图片

avaterUrl是头像的地址,一般都存放在阿里云中

代码实现

controller

@URL是数据校验是否是一个url格式的字符串,防止胡乱上传

    @PatchMapping("/updateAvatar")
    public Result updateAvatar(@RequestParam @URL String avatarUrl){
        userService.updateAvatar(avatarUrl);

        return Result.success(avatarUrl);
    }

service

直接从mapper中获取当前登录人token中的id

    @Override
    public void updateAvatar(String avatarUrl) {
        Map 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);

测试

做一个springboot用户信息模块_第9张图片

4、更新用户密码

做一个springboot用户信息模块_第10张图片

前言

        同样地,更新密码也是用户信息整体的一部分,所以用PATCH请求方式

        在之前我们更新用户基本信息的时候,传递的参数是一个user实体类参数,且json字段名和user属性名一摸一样。但是在更新密码中,有一个new_pwd和old_pwd来表示新密码和旧密码,user类中就没有这个属性了,添加进去在逻辑上也不太合适。所以我们可以通过传递一个map集合来表示这个参数。

        由于我们的密码在数据库中已经经过了MD5加密,所以直接拿传递过来的参数与数据库中的密码比对是不行的,可以先对参数进行MD5加密,然后在与数据库数据进行比对检验是否正确。

代码实现

Controller

    @PatchMapping("/updatePwd")
    public Result updatePwd(@RequestBody Map 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 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

    @Override
    public void updatePwd(String newPwd) {
        Map 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);

测试

做一个springboot用户信息模块_第11张图片

做一个springboot用户信息模块_第12张图片

做一个springboot用户信息模块_第13张图片

你可能感兴趣的:(spring,boot,后端,java)