本文最后更新于778 天前,其中的信息可能已经过时,如有错误请发送邮件到3196812536@qq.com
前言
上回书说到登录接口的实现。
现在来实现系统主要功能中的获取用户信息这个功能。
正文
需要注意的是,在获取用户信息的时候不单单只是涉及到数据库信息的查询,还需要涉及到在不同页面间用户信息的同步。有点类似服务器的session。
前置知识
线程指的是在一个进程(Process)中独立执行的一段指令序列。简单来说,一个进程可以包含多个线程,每个线程都拥有自己的执行流程和栈,但它们共享进程的内存空间和资源。线程的引入使得程序可以并发执行多个任务,提高了程序的效率和响应速度。
Tomcat Java Web服务器中存在线程池,在使用ThreadLocal后(后面会提到)如果不及时关停,可能会导致内存泄漏——线程的存在时间很长,因为没有多余的线程导致该线程直接被下一个用户使用,最后导致用户信息的外传。
可跳过部分
用户登录后需要调用用户提交储存的个人信息,然而每次调用都会需要书写查询的代码,会大大增加代码的篇幅。比如说下面的代码:
@GetMapping("/userinfo")
public Result<User> userinfo(@RequestHeader(name = "Authorization") String token){
//根据用户名查询用户
Map<String, Object> map = JwtUtil.parseToken(token);
String username = (String) map.get("username");
Map<String, Object> map = ThreadLocalUtil.get();
String username = (String) map.get("username");
User user = userService.findByUserName(username);
return Result.success(user);
}
这时候就需要使用到ThreadLocal工具来进行线程(个人信息)的同步。
需要注意的点
返回的信息中上传信息和更新信息的时间由于在数据库中命名方式的不同(驼峰命名和下划线命名的差异),所以可能输出为null。需要在配置文件application.yml中做出如下的修改。
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/big_event
username: root
password: root
mybatis:
configuration:
map-underscore-to-camel-case: true #开启驼峰命名和下划线命名的自动转换
还有就是输出的信息中不能蕴含用户密码的MD5值。所以就需要对pojo文件夹下的User文件添加如下修改
package org.example.pojo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.time.LocalDateTime;
//lombok 在编译阶段,为实体类自动生成setter getter toString
//pom 文件中引入依赖 在实体类上添加注释
@Data
public class User {
private Integer id;//主键ID
private String username;//用户名
@JsonIgnore//让springmvc把当前对象转换成json字符串的时候忽略password
private String password;//密码
private String nickname;//昵称
private String email;//邮箱
private String userPic;//用户头像地址
private LocalDateTime createTime;//创建时间
private LocalDateTime updateTime;//更新时间
}
代码展示
ThreadLocalUtil.java的内容
package org.example.utils;
import java.util.HashMap;
import java.util.Map;
/**
* 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();
}
}
现修改UserController.java为下面的内容
package org.example.controller;
import jakarta.validation.constraints.Pattern;
import org.example.pojo.Result;
import org.example.pojo.User;
import org.example.service.UserService;
import org.example.utils.JwtUtil;
import org.example.utils.Md5Util;
import org.example.utils.ThreadLocalUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/user")
@Validated
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public Result register(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password) {
//查询用户
User u = userService.findByUserName(username);
if (u == null) {
//没有占用
//注册
userService.register(username, password);
return Result.success();
} else {
//占用
return Result.error("用户名已被占用");
}
//注册
}
@PostMapping("/login")
public Result<String> login(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password){
//根据用户名查询用户
User loginUser = userService.findByUserName(username);
//判断该用户是否存在
if (loginUser==null){
return Result.error("用户名不存在");
}
//判断密码是否正确,传入参数加密后与数据库中的密文进行比对
if (Md5Util.getMD5String(password).equals(loginUser.getPassword())) {
//登录成功
Map<String,Object> claims = new HashMap<>();
claims.put("id",loginUser.getId());
claims.put("username",loginUser.getUsername());
String token = JwtUtil.genToken(claims);
return Result.success(token);
}
return Result.error("密码错误");
}
@GetMapping("/userinfo")
public Result<User> userinfo(/*@RequestHeader(name = "Authorization") String token*/){
//根据用户名查询用户
/*Map<String, Object> map = JwtUtil.parseToken(token);
String username = (String) map.get("username");*/
Map<String, Object> map = ThreadLocalUtil.get();//修改添加的部分
String username = (String) map.get("username");
User user = userService.findByUserName(username);
return Result.success(user);
}
}
修改为LoginController.java下面的内容(在最后添加了线程的清除)
package org.example.interceptors;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.example.pojo.Result;
import org.example.utils.JwtUtil;
import org.example.utils.ThreadLocalUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.Map;
@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<String, Object> claims = JwtUtil.parseToken(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 {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
//清空ThreadLocal中的数据
ThreadLocalUtil.remove();
}
}
最终实现的效果就是这样

尾声
接下来就是更新用户信息的功能了。
大家可以点个关注跟进一下。


