会话技术
会话技术:在服务器端创建一个与客户端浏览器相关的数据,用来记录客户端浏览器的访问信息。
会话:用户打开浏览器,访问Wb服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。
会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
会话跟踪方案:
- 客户端会话跟踪技术:Cookie
- 服务器端会话跟踪技术:Session
- 令牌技术
Cookie
优点:
- HTTP协议中支持的技术
缺点:
- 存储空间有限,通常情况下不能存储二进制数据
- 不能跨域访问
- 不安全,用户可以自己禁用Cookie
Session
优点:
- 部署在服务器,安全
缺点;
- 服务器集群环境下无法直接使用Session
- Cookie的缺点
JWT令牌技术
JWT令牌技术:JSON Web Token,是一种用于在网络应用环境间传递声明的开放标准(RFC 7519)。
- 全称称:JSON Web Token(https://jwt.io/)
- 定义了一整简洁的、自包含的格式,用于在通信双法以JSON数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。
- 组成:
- 第一部分:Header(头部),记录令牌类型、签名算法等。例如:{“alg”:”HS256”,”typ”:”JWT”}
- 第二部分:Payload(有效负载),携带一些自定义信息】默认信息等。例如:{“id”:1,”name”:”zhangsan”}
- 第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、.payload,并加入指定秘钥,通过指定签名算法计算而来。
第一和第二部分内容都是明文自定义的内容,第三部就像一把钥匙,使用Signature封装明文内容,解析也必须验证签名。
使用JWT技术实现登陆认证
引入依赖
1 2 3 4 5
| <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
|
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package com.nodaoli.utils;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; import java.util.Map;
public class JwtUtils {
private static String signKey = "nodaoli"; private static Long expire = 43200000L;
public static String generateJwt(Map<String, Object> claims){ String jwt = Jwts.builder() .addClaims(claims) .signWith(SignatureAlgorithm.HS256, signKey) .setExpiration(new Date(System.currentTimeMillis() + expire)) .compact(); return jwt; }
public static Claims parseJWT(String jwt){ Claims claims = Jwts.parser() .setSigningKey(signKey) .parseClaimsJws(jwt) .getBody(); return claims; } }
|
过滤器Filter
概述
- 概念:Filter过滤器,是」JavaWeb三大组件(Servlet、Filter、Listener)之一。
- 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。
- 过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等。
重写Filter接口的doFilter方法,进行放行操作,调用filterChain
对象的doFilter()
方法。
基本上就是用到了传入的三个参数。
1 2 3 4 5 6
| @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("拦截到了请求"); filterChain.doFilter(servletRequest,servletResponse); }
|
拦截路径
1 2 3
| @WebFilter(urlPatterns = "/*",filterName = "myFilter") public class MyFilter implements Filter { }
|
拦截路径 |
urlPatterns值 |
含义 |
拦截具体路径 |
/login |
只有访问/login路径时,才会拦截 |
目录拦截 |
/emps/* |
访问/emps目录下的所有资源时,都会拦截 |
拦截所有 |
/* |
访问所有资源时,都会拦截 |
过滤器链
介绍:一个web应用中,可以定义多个过滤器,这多个过滤器就形成了过滤器链。
顺序:注解配置的Filter,优先级是按照过滤器类名(字符串)的自然排序。
例子:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| import com.alibaba.fastjson.JSONObject; import com.nodaoli.pojo.Result; import com.nodaoli.utils.JwtUtils; import jakarta.servlet.*; import jakarta.servlet.annotation.WebFilter; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils;
import java.io.IOException;
@Slf4j @WebFilter(urlPatterns = "/*") public class LoginCheckFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletRequest; HttpServletResponse resp = (HttpServletResponse) servletResponse;
String url = req.getRequestURL().toString(); log.info("请求的url:{}",url);
if (url.contains("login")) { log.info("放行"); filterChain.doFilter(servletRequest,servletResponse); return; }
String jwt = req.getHeader("token");
if (!StringUtils.hasLength(jwt)) { log.info("请求头token为空"); Result error = Result.error("NOT_LOGIN");
String notLogin = JSONObject.toJSONString(error); resp.getWriter().write(notLogin); return; }
try { JwtUtils.parseJWT(jwt); } catch (Exception e) { e.printStackTrace(); log.info("令牌解析失败,放回未登录信息"); Result error = Result.error("NOT_LOGIN"); String notLogin = JSONObject.toJSONString(error); resp.getWriter().write(notLogin); return; }
log.info("令牌合法,放行"); filterChain.doFilter(servletRequest,servletResponse); } }
|
拦截器Interceptor
概述
- 概念:是一种动态拦截方法调用的机制,类似于过滤器。Spig框架中提供的,用来动态拦截控制器方法的执行。
- 作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。
创建
如果你需要自定义 Interceptor 的话必须实现 org.springframework.web.servlet.HandlerInterceptor
接口或继承 org.springframework.web.servlet.handler.HandlerInterceptorAdapter
类,并且需要重写下面下面 3 个方法:
preHandler(HttpServletRequest request, HttpServletResponse response, Object handler)
方法在请求处理之前被调用。该方法在 Interceptor 类中最先执行,用来进行一些前置初始化操作或是对当前请求做预处理,也可以进行一些判断来决定请求是否要继续进行下去。该方法的返回至是 Boolean 类型,当它返回 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当它返回为 true 时会继续调用下一个 Interceptor 的 preHandle 方法,如果已经是最后一个 Interceptor 的时候就会调用当前请求的 Controller 方法。
postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
方法在当前请求处理完成之后,也就是 Controller 方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作。
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)
方法需要在当前对应的 Interceptor 类的 postHandler 方法返回值为 true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行。此方法主要用来进行资源清理。
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 26 27 28 29 30 31 32
| public class LogInterceptor extends HandlerInterceptorAdapter {
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { long startTime = System.currentTimeMillis(); System.out.println("\n-------- LogInterception.preHandle --- "); System.out.println("Request URL: " + request.getRequestURL()); System.out.println("Start Time: " + System.currentTimeMillis());
request.setAttribute("startTime", startTime);
return true; }
@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("\n-------- LogInterception.postHandle --- "); System.out.println("Request URL: " + request.getRequestURL()); }
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("\n-------- LogInterception.afterCompletion --- ");
long startTime = (Long) request.getAttribute("startTime"); long endTime = System.currentTimeMillis(); System.out.println("Request URL: " + request.getRequestURL()); System.out.println("End Time: " + endTime);
System.out.println("Time Taken: " + (endTime - startTime)); } }
|
应用
创建一个配置类
1 2 3 4 5 6 7 8 9
| @Configuration public class WebConfig implements WebMvcConfigurer {
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LogInterceptor()); } }
|
拦截器路径addPathPatterns("/**")
直接加在addInterceptor()
方法中即可。
使用excludePathPatterns()
方法可以排除某些路径。