本文介绍了Cookie,Session,JWT,过滤器,拦截器的相关知识
浏览器请求头携带Cookie;
服务器响应头Set-Cookie;
进行会话跟踪。
但是移动端APP,用户禁用,跨域情况下,Cookie失效。
跨域:协议、IP/域名、端口不一样,前端请求时,出现跨域
Session:
浏览器请求头的Cookie中携带JSessionId;
服务器响应头的Cookie中有JSessionId
服务器集群需要处理Session共享的问题
分为以下三个部分,Header和PayLoad使用Base64编码
Header:存储令牌类型,加密算法。比如{“alg”: “HS256”, “type”: “JWT”}
PayLoad :Json格式的数据。比如:{“userId”: 1}
Signature: 签名,对Header,PayLoad根据加密算法进行加密
pom.xml引入依赖:
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
<version>0.9.1version>
dependency>
JWT加解密单元测试:
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 {
@Test
public void testBuildJwt(){
Map<String, Object> claims = new HashMap<>();
claims.put("username", "zhangsan");
claims.put("job", "developer");
String compact = Jwts.builder()
//设置有效载荷
.setClaims(claims)
//设置签名
.signWith(SignatureAlgorithm.HS256, "123456")
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 7))
.compact();
System.out.println(compact);
}
@Test
public void testParseJwt(){
Claims body = Jwts.parser()
.setSigningKey("123456")
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJqb2IiOiJkZXZlbG9wZXIiLCJleHAiOjE3MTMzMjE0MjQsInVzZXJuYW1lIjoiemhhbmdzYW4ifQ.R9H8nncSCooaffujgxlCk3vuikkE-sH9j8ATGWQu-y8")
.getBody();
System.out.println(body);
}
}
<dependency>
<groupId>com.auth0groupId>
<artifactId>java-jwtartifactId>
<version>4.4.0version>
dependency>
定义配置文件类,这里定义配置文件中,前缀为jwt,变量名称成secret的变量。
配置文件中配置:jwt.secret=xxx
package com.web.springbootall.property;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "jwt")
@Data
public class JwtSecretProperty {
private String secret;
}
定义工具类
package com.web.springbootall.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.web.springbootall.property.JwtSecretProperty;
//import io.jsonwebtoken.Claims;
//import io.jsonwebtoken.Jwts;
//import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.Map;
@Component
public class JwtUtil {
@Autowired
private JwtSecretProperty jwtSecretProperty;
public String generate(Map<String, Object> claims) {
return JWT.create().withClaim("user", claims).withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 3600))
.sign(Algorithm.HMAC256(jwtSecretProperty.getSecret()));
// return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, jwtSecretProperty.getSecret())
// .setExpiration(new Date(System.currentTimeMillis() + 1000 * 3600)).compact();
}
public Claim parse(String token) {
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecretProperty.getSecret())).build();
return jwtVerifier.verify(token).getClaim("user");
// return Jwts.parser().setSigningKey(jwtSecretProperty.getSecret()).parseClaimsJws(token).getBody();
}
}
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 过滤器的业务逻辑
System.out.println("DemoFilter doFilter");
// 放行
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
@ServletComponentScan
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@ServletComponentScan
@SpringBootApplication
public class TliasWebManagementApplication {
public static void main(String[] args) {
SpringApplication.run(TliasWebManagementApplication.class, args);
}
}
引入JSON依赖:
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>2.0.28version>
dependency>
过滤器实现登陆验证:
import com.alibaba.fastjson.JSON;
import com.qinjie.tliaswebmanagement.domain.Result;
import com.qinjie.tliaswebmanagement.util.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
@Slf4j
public class LoginFilter implements Filter {
@Autowired
private JwtUtil jwtUtil;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//如果不是登陆接口,并且token不对,则拦截,退出
HttpServletRequest request = (HttpServletRequest) servletRequest;
if (!request.getRequestURI().equals("/login")){
String token = request.getHeader("token");
Claims claims = null;
try {
claims = jwtUtil.parse(token);
} catch (Exception e) {
log.error("令牌解析失败,token:{}", token, e);
}
if (token == null || claims == null){
Result notLogin = Result.fail("NOT_LOGIN");
String notLoginString = JSON.toJSONString(notLogin);
servletResponse.getWriter().write(notLoginString);
return;
}
}
// 放行
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
定义Spring管理(@Component
)的类,实现HandlerInterceptor
接口的preHandle
方法,返回true放行
import com.alibaba.fastjson.JSON;
import com.qinjie.tliaswebmanagement.domain.Result;
import com.qinjie.tliaswebmanagement.util.JwtUtil;
import io.jsonwebtoken.Claims;
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 JwtUtil jwtUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!request.getRequestURI().equals("/login")){
String token = request.getHeader("token");
Claims claims = null;
boolean hasToken = StringUtils.hasLength(token);
try {
if (hasToken) {
claims = jwtUtil.parse(token);
}
} catch (Exception e) {
log.error("令牌解析失败,token:{}", token, e);
}
if (!hasToken || claims == null){
Result notLogin = Result.fail("NOT_LOGIN");
String notLoginString = JSON.toJSONString(notLogin);
response.getWriter().write(notLoginString);
return false;
}
}
return true;
}
}
定义MVC配置类(@Configuration
),实现WebMvcConfigurer
接口的addInterceptors
方法,将拦截器加入其中(addInterceptor
),设定拦截路径addPathPatterns
,放行路径excludePathPatterns
。比如下面将/login
放行
import com.qinjie.tliaswebmanagement.interceptor.LoginInterceptor;
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 WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/login", "/css/**", "/js/**", "/fonts/**", "/images/**");
}
}
过滤器在DispatcherServlet前执行,可以拦截所有资源;
拦截器在DispatcherServlet后执行,仅拦截Spring的资源。
过滤器和拦截器可以用来做登陆校验,登陆过才放行请求到接口,而不需要所有接口都加上校验逻辑。
出现报错:
Handler dispatch failed: java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter
,
添加JWT相关的依赖:
<dependency>
<groupId>javax.xml.bindgroupId>
<artifactId>jaxb-apiartifactId>
<version>2.3.1version>
dependency>