学习进度条
今日所花时间:一小时
今日代码量:150行
博客量:一篇
了解到的知识点:JWT令牌 ThreadLocal优化 参数校验 加密工具类Md5Util
登录认证
//登录认证
//令牌就是一段字符串
//承载业务数据,减少后续请求查询数据库的次数
//放篡改,保证信息的合法性和有效性
//Base64是一种基于64个可打印字符来表示二进制数据的编码方式
//jwt组成
// 第一部分:header(头) 记录令牌类型,签名算法等
// 第二部分:payload(有效载荷)携带默认信息
// 第三部分:Signature(签名)防止Token被篡改,确保安全性,将header、payload,并加入指定密钥通过指定签名算法计算而来
JwtTest
package com.example.day0225;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.junit.jupiter.api.Test;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtTest {
// JWT生成
@Test
public void testGen (){
Map<String,Object> claims = new HashMap<>();
claims.put("id",1);
claims.put("username","张三");
//生成jwt的代码
String token = JWT.create ()
.withClaim("user",claims)//添加载荷
.withExpiresAt(new Date(System.currentTimeMillis()+1000*60*60+12))//添加过期日期
.sign(Algorithm.HMAC256("goodOneNuo"));//指定算法,配置密钥
System.out.println(token);
}
// JWT的验证
// JWT校验时使用的签名秘钥,必须和生成WT令牌时使用的秘钥是配套的。
// 如果WT令牌解析校验时报错,则说明JWT令牌被篡改或失效了,令牌非法。
@Test
public void testParse() {
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" +
".eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6IuW8oOS4iSJ9LCJleHAiOjE3NDEwNjI0NjN9" +
".BApxYQ86tH4hV51kPPMYs2ETPgFMBkBC576XQqaOpLQ";
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("goodOneNuo")).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);//验证token,生成一个解析后的JWT对象
Map<String, Claim> claims = decodedJWT.getClaims();
System.out.println(claims.get("user"));
}
//如果篡改了头部和载荷部分的数据,那么验证失败
//如果密钥更改,验证失败
//token过期
}
JwtUtil
package com.example.day0225.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtil {
private static final String KEY = "itheima";
//接收业务数据,生成token并返回
public static String genToken(Map<String, Object> claims) {
return JWT.create()
.withClaim("claims", claims)
.withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))
.sign(Algorithm.HMAC256(KEY));
}
//接收token,验证token,并返回业务数据
public static Map<String, Object> parseToken(String token) {
return JWT.require(Algorithm.HMAC256(KEY))
.build()
.verify(token)
.getClaim("claims")
.asMap();
}
}
ThreadLocalTest
package com.example.day0225;
import org.junit.jupiter.api.Test;
public class ThreadLocalTest {
// ThreadLocal 提供线程局部变量
// 用来存取数据:set()/get()
// 使用ThreadLocal存储的数据,线程安全
@Test
public void testThreadLocalSetAndGet() {
//提供一个ThreadLocal对象
ThreadLocal tl = new ThreadLocal();
//开启两个线程
new Thread(()->{
tl.set("消炎");
System.out.println(Thread.currentThread().getName() +":"+ tl.get());
System.out.println(Thread.currentThread().getName() +":"+ tl.get());
System.out.println(Thread.currentThread().getName() +":"+ tl.get());
},"蓝色").start();
new Thread(()->{
tl.set("止咳");
System.out.println(Thread.currentThread().getName() +":"+ tl.get());
System.out.println(Thread.currentThread().getName() +":"+ tl.get());
System.out.println(Thread.currentThread().getName() +":"+ tl.get());
},"绿色").start();
}
}
ThreadLocal工具类
package com.example.day0225.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();
}
}
MdUtil工具类
package com.example.day0225.utils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Md5Util {
/**
* 默认的密码字符串组合,用来将字节转换成 16 进制表示的字符,apache校验下载的文件的正确性用的就是默认的这个组合
*/
protected static char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
protected static MessageDigest messagedigest = null;
static {
try {
messagedigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException nsaex) {
System.err.println(Md5Util.class.getName() + "初始化失败,MessageDigest不支持MD5Util。");
nsaex.printStackTrace();
}
}
/**
* 生成字符串的md5校验值
*
* @param s
* @return
*/
public static String getMD5String(String s) {
return getMD5String(s.getBytes());
}
/**
* 判断字符串的md5校验码是否与一个已知的md5码相匹配
*
* @param password 要校验的字符串
* @param md5PwdStr 已知的md5校验码
* @return
*/
public static boolean checkPassword(String password, String md5PwdStr) {
String s = getMD5String(password);
return s.equals(md5PwdStr);
}
public static String getMD5String(byte[] bytes) {
messagedigest.update(bytes);
return bufferToHex(messagedigest.digest());
}
private static String bufferToHex(byte bytes[]) {
return bufferToHex(bytes, 0, bytes.length);
}
private static String bufferToHex(byte bytes[], int m, int n) {
StringBuffer stringbuffer = new StringBuffer(2 * n);
int k = m + n;
for (int l = m; l < k; l++) {
appendHexPair(bytes[l], stringbuffer);
}
return stringbuffer.toString();
}
private static void appendHexPair(byte bt, StringBuffer stringbuffer) {
char c0 = hexDigits[(bt & 0xf0) >> 4];// 取字节中高 4 位的数字转换, >>>
// 为逻辑右移,将符号位一起右移,此处未发现两种符号有何不同
char c1 = hexDigits[bt & 0xf];// 取字节中低 4 位的数字转换
stringbuffer.append(c0);
stringbuffer.append(c1);
}
}
参数校验
// 实体参数校验
// 第一步实体类的成员变量上添加注解@NotNull/@NotEmpty/@Email
// 接口方法的实体参数上添加@Validated注解
实例操作:
package com.example.day0225.pojo;
//import net.minidev.json.annotate.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDateTime;
public class User {
// 实体参数校验
// 第一步实体类的成员变量上添加注解@NotNull/@NotEmpty/@Email
// 接口方法的实体参数上添加@Validated注解
@NotNull//值不能为null
private Integer id;//主键ID
private String username;//用户名
@JsonIgnore//让springmvc把当前对象转换成json字符串的时候,忽略password,最终的json字符串中就没有password这个属性了
private String password;//密码
@NotEmpty//值不能为null并且内容不为空
@Pattern(regexp = "^\\S{1,10}$")
private String nickname;//昵称
@NotEmpty
@Email//满足邮箱格式
private String email;//邮箱
private String userPic;//用户头像地址
private LocalDateTime createTime;//创建时间
private LocalDateTime updateTime;//更新时间
public User(LocalDateTime updateTime, LocalDateTime createTime, String userPic, String email, String nickname, String password, String username, Integer id) {
this.updateTime = updateTime;
this.createTime = createTime;
this.userPic = userPic;
this.email = email;
this.nickname = nickname;
this.password = password;
this.username = username;
this.id = id;
}
public User() {
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public String getUserPic() {
return userPic;
}
public void setUserPic(String userPic) {
this.userPic = userPic;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
package com.example.day0225.controller;
import com.example.day0225.pojo.Result;
import com.example.day0225.pojo.User;
import com.example.day0225.service.UserService;
import com.example.day0225.utils.JwtUtil;
import com.example.day0225.utils.Md5Util;
import com.example.day0225.utils.ThreadLocalUtil;
import jakarta.validation.constraints.Pattern;
import org.hibernate.validator.constraints.URL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
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 = map.get("username").toString();
User user = userService.findByUserName(username);
return Result.success(user);
}
@PutMapping("/update")
public Result update (@RequestBody @Validated User user){
userService.update(user);
return Result.success();
}
// @URL对传进来的参数进行校验,判断是否为网页图片链接
@PatchMapping("/updateAvatar")
public Result updateAvatar(@RequestParam @URL String avatarUrl){
userService.updateAvatar(avatarUrl);
return Result.success();
}
@PatchMapping("/updatePwd")
public Result updatePwd(@RequestBody Map<String,String> params) {
//1.校验参数
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("缺少必要参数");
}
//校验原密码
//调用userService根据用户名拿到原密码,再和old_Pwd比对
Map<String,Object> map = ThreadLocalUtil.get();
String username = map.get("username").toString();
User loginUser = userService.findByUserName(username);
if (!loginUser.getPassword().equals(Md5Util.getMD5String(oldPwd))){
return Result.error("原密码填写不正确");
}
//newPwd和rePwd是否一致
if(!rePwd.equals(newPwd)){
return Result.error("两次填写的新密码不一致");
}
//2.调用service完成密码更新
userService.updatePwd(newPwd);
return Result.success();
}
}

浙公网安备 33010602011771号