需求
在后端管理系统中,需要登录后才能访问后端系统页面,没有登录则跳转到登录页面进行登录,那么如何对用户请求做校验、如何拦截非法请求?
一、JWT令牌
JWT(JSON Web Token)是一个简单的字符串,可以在请求参数或者是请求头当中直接传递。可以在jwt令牌中存储用户的相关信息,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。
JWT由三部分组成:
第一部分:记录令牌类型、签名算法。
第二部分:有效载荷。即用户相关的数据
第三部分:签名算法。防止token被篡改,对前两部分根据指定密钥进行加密。
实现登录校验的基本流程
1、要进入到后台管理系统,首先完成登录操作,登录成功后,服务端生成一个JWT令牌,并且返回给前端,前端将JWT令牌存储到本地。
2、在后续的每次请求中,前端都会将JWT令牌传到服务端,服务端的拦截器会对请求进行拦截,
拦截需要考虑到各种情况:登录页面不需要拦截
3、然后校验令牌的有效性,如果无效,则返回一个错误信息不放行请求,并跳转到登录页面,校验成功后,放行请求,跳转到后台页面。
1、引入jwt依赖
<!-- JWT依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2、jwt工具类
application.yml
jwt:
secret: zxyzxyzxy
timeout: 60
工具类
有两个方法,一个方法生成token,另一个方法校验token
@Component
public class JWTUtil {
//指定签名密钥
@Value("${jwt.secret}")
private String jwtSecret;
//指定过期时间
@Value("${jwt.timeout}")
private long timeout;
public String generateJWT(Map<String,Object> claims){
//加密令牌
String jwt = Jwts.builder()
.signWith(SignatureAlgorithm.HS256,jwtSecret)//签名算法,不能太短
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis()+1000 * 60 * timeout))//过期时间为60分钟
.compact();//生成
return jwt;
}
public Map<String,Object> parseJWT(String jwt){
//返回有效载荷信息
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(jwt)
.getBody();
return claims;
}
}
3、使用jwt生成token
//设置jwt令牌
//存储有效载荷(用户信息)
Map<String,Object> map = new HashMap<>();
map.put("id",loginInfo.getId());
map.put("username",loginInfo.getUsername());
//生成token
String jwt = jwtUtil.generateJWT(map);
loginInfo.setToken(jwt);
二、过滤器
过滤器位于客户端和Servlet之间,可以拦截整个Web应用的所有请求 。
1、定义过滤器
doFilter方法:这个方法是在每一次拦截到请求之后都会被调用,所以这个方法是会被调用多次的,每拦截到一次请求就会调用一次doFilter()方法。
@WebFilter
,并指定属性urlPatterns
,通过这个属性指定过滤器要拦截哪些请求,/*表示拦截所有请求
@Slf4j
@Component
@WebFilter(urlPatterns = "/*")
public class LoginFilter implements Filter {
@Autowired
private JWTUtil jwtUtil;
//拦截到请求时,调用该方法
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String url = request.getRequestURI();//请求地址
log.info("请求地址:" + url);
//如果是登录请求直接放行
if(url.equals("/login")){
filterChain.doFilter(request, response);//放行
return;
}
//拦截其他请求,判断token
String token = request.getHeader("token");
if(token == null){
response.setStatus(401);
return;
}
try {
//解析token
Map<String, Object> map = jwtUtil.parseJWT(token);
log.info(map.get("username") + "登录");
filterChain.doFilter(request, response);
}catch (Exception e){
response.setStatus(401);
}
}
}
2、启动类开启对Servlet的支持
@ServletComponentScan //开启对Servlet组件的支持
@SpringBootApplication
public class TliasManagementApplication {
public static void main(String[] args) {
SpringApplication.run(TliasManagementApplication.class, args);
}
}
三、拦截器(第二种拦截方式)
过滤器和拦截器选择一种使用即可。
拦截器位于Servlet和控制器之间,是SpringMVC提供的,用于拦截Spring环境的请求 ,拦截器属于SpringMVC,依赖于Spring容器 。
1、定义拦截器
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private JWTUtil jwtUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String uri = request.getRequestURI();//获取请求地址
//login地址在webmvc过滤
//拦截其他请求
String token = request.getHeader("token");
if(token == null){
response.setStatus(401);
return false;
}
//判断token是否有效
try {
Map<String, Object> stringObjectMap = jwtUtil.parseJWT(token);
return true;
}catch (Exception e){
response.setStatus(401);
return false;
}
}
}
2、注册配置拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//拦截所有请求,放行/login请求
registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/login");
}
}