spring项目 登录篇-黑马vue+spring项目学习笔记

登录认证

目标:让为登录的用户无法访问特定网页

JWT令牌

  令牌是一段字符串

  • 承载业务数据,减少后续请求查询数据库的次数
  • 防篡改,保证信息的合法性和有效性

  • 全称 JSON Web Token(https://jwt.io/)
  • 定义了一种简洁的、自包含的格式、用于通信双方以json格式安全的传输信息。
  • 组成
    • 第一部分:Header,记录令牌类型、签名算法等。
    • 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息。
    • 第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定密钥,通过与指定签名算法而来。
    • 通过Base64进行编码

JWT相关依赖

1
2
3
4
5
<dependency>  
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>

JWT令牌的生成

1
2
3
4
5
6
7
8
9
10
11
12
void genToke(){  
Map<String,Object> claims = new HashMap<>();
claims.put("id",1);
claims.put("username","zhangsan");
String Token = JWT.create()
.withClaim("user", claims)
.withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))
.sign(Algorithm.HMAC256("密钥"));

System.out.println(Token);

}

JWT令牌验证

1
2
3
4
5
6
7
8
9
10
@Test
void parseToken(){
String token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"+
".eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6InpoYW5nc2FuIn0sImV4cCI6MTcxOTUzNDgyNn0"+
".RPFa6dpp3hYTAqsiBUnwkITSn9XWNBAnFhYacuuJBbw";
JWTVerifier jwtverifier = JWT.require(Algorithm.HMAC256("密钥")).build();
DecodedJWT decodedJWT = jwtverifier.verify(token);
Map<String, Claim> claimMap = decodedJWT.getClaims();
System.out.println(claimMap.get("user"));
}

JWT工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 = "lingzxl";

//接收业务数据,生成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();
}
}

前置拦截器Token验证

  1. 创建interceptors
  2. 创建LoginInterceptor类并实现HandlerInterceptor
  3. 重写preHandle方法 >参考代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component  
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
try{
Map<String, Object> claims = JwtUtil.parseToken(token);
} catch (Exception e){
//登录失败
//设置http响应码为401
response.setStatus(401);
return false;
}
//登录成功
return true;
}
}
  1. 注册拦截器
1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration  
public class WebConfig implements WebMvcConfigurer {
private LoginInterceptor loginInterceptor;
@Autowired
public void setLoginInterceptor(LoginInterceptor loginInterceptor){
this.loginInterceptor=loginInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
//登录和注册接口不拦截
registry.addInterceptor(loginInterceptor).excludePathPatterns("/user/login","/user/register");
}
}

使得返回json数据是不传出password参数

1
2
@JsonIgnore  
private String password;

ThreadLocal

提供线程局部变量

  • 用于存取数据:set()/get()
  • 使用Threadloacl存储的数据,线程安全
  • 用完记得调用remove方法释放

使用原因

  使得解析Token之类的操作只进行一次,减少计算资源的消耗。

工具代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();
}
}

后置拦截器

  • 用于清除tomcat服务完一个请求后本地线程里的变量,防止内存泄漏
1
2
3
4
5
6
//后置拦截器  
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//清除ThreadLocal中的数据
ThreadLocalUtil.remove();
}

更新用户基本信息

  • 使用@PutMapping("/update")接收
  • 在接受参数处写上@RequestBody用于把前端传入的json数据转换成User对象类