我们的系统目前在不经过登录的情况下,直接输入员工页面地址就可以访问,这是非常不安全的。
正确的流程应该是:当访问请求到达服务器后,服务器要校验当前用户是否已经登录过
如果登录过,就放行请求
如果未登录过,就禁止请求访问
那如何知道用户是否已经登录过呢?这就需要在用户登录成功后,由服务器为其颁发一个token(身份标识)
然后后面用户每次发送请求,都会携带着这个token
而作为系统会对每次的请求进行拦截,校验token的合法性即可
JWT
介绍
全称:JSON Web Token (JSON Web Tokens - jwt.io),用于对应用程序上的用户进行身份标记
本质上就是一个经过加密处理与校验处理的字符串,它由三部分组成:
头信息(Header):记录令牌类型和签名算法,例如:{"alg": "HS256","typ": "JWT"}
有效载荷(Payload):记录一些自定义能够区分身份的非敏感信息,例如:{"id": "1","username": "tom"}
签名(Signature):用于保证Token在传输过程中不被篡改,它是header、payload,加入指定算法计算得来的
代码测试
① 在pom.xml中引入依赖
<!--Token生成与解析-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
② 生成并校验token
package com.itheima.test;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.jupiter.api.Test;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtTest {
//生成token
@Test
public void genJwt() {
Map<String, Object> claims = new HashMap<>();
claims.put("id", 1);
claims.put("username", "Tom");
String jwt = Jwts.builder().
setClaims(claims) //自定义内容(载荷)
.signWith(SignatureAlgorithm.HS256, "itheima") //签名算法和盐
.setExpiration(new Date(System.currentTimeMillis() + 12 * 3600 * 1000)) //有效期
.compact();
System.out.println(jwt);
}
//校验token
@Test
public void checkJwt() {
Claims claims = Jwts.parser()
.setSigningKey("itheima")//盐
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjc3MzU3MjE0LCJ1c2VybmFtZSI6IlRvbSJ9.RBtRZGHUefLElDWWIlQRoy0_Dl71sZysPP61vVa46oo")//上一步得到的值
.getBody();
System.out.println(claims);
}
}
拦截器
介绍
拦截器是Spring提供的一种技术,它的功能似于过滤器,它会在进入controller之前,离开controller之后以及响应离开服务时进行拦截。
拦截器实现访问校验
<!--添加依赖-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
① 创建Interceptor
package com.itheima.Interceptor;
import com.google.gson.Gson;
import com.itheima.util.JwtUtils;
import com.itheima.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private Gson gson;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1. 获取请求头中的令牌(token)
String token = request.getHeader("token");
//2. 判断令牌是否存在,如果不存在,返回错误结果(未登录)。
if (!StringUtils.hasLength(token)){
log.info("token不存在");
//返回错误消息
String json = gson.toJson(Result.error("NOT_LOGIN"));
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
return false;//结束判断
}
//3. 解析token,如果解析失败,返回错误结果(未登录)。
try {
JwtUtils.parseJWT(token);
}catch (Exception e){
log.info("token错误");
//返回错误消息
String json = gson.toJson(Result.error("NOT_LOGIN"));
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
return false;//结束判断
}
//4.放行
return true;
}
}
② 配置Interceptor
package com.itheima.config;
import com.itheima.interceptor.LoginCheckInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Autowired
private LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginCheckInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/login");
}
}