# 前后端分离 token 验证

jwt token 常在请求头中进行传递,前端可以保存在 cookie 中,然后前端发送请求的时候从 cookie 中取出,放在请求头中发送请求,后端可以直接从请求中 request.getHead("token") 获取到 token

前后端分离不代表就不能用 session 验证是否登录,但是会出现分布式 session 共享问题。
面试题常考:详细介绍一下 JWT,除了 JWT 方式外,还有什么鉴权方式?JWT 方式鉴权和 Session-Cookie 方式鉴权方式的对比及优缺点?前后端分离就不能使用 Session-Cookie 吗?

token 验证完整流程

jwt流程
详细流程如图所示
细节①:当访问需要登录之后才有的接口时候,需要走拦截器,判断是否携带了 token,是否登录,如果未携带不放行,并让此登录
细节②:由于用户访问用户相关 api 的时候会一直走 token,可能会出现登录的时候 token 未失效但是后面一直在逛购物车,逛订单首页,然后 token 失效了,所系需要进行刷新 token

细节③:绝大部分接口是不需要知道 id 是谁的,因此直接通过前端传递的信息就够用了,不需要纠结 id 到底从哪里获得

细节④:可以在工具类里面实现静态方法,根据 HTTP 请求里面的 token,获取到用户信息,这样就可以写业务的时候直接调用,然后获得用户的信息,即使前端没有传递。

优化:可以在登录的时候,在生成 token 以后,把用户信息存放到 redis 中,这样注入 HttpSeveletRequest , 然后获取 token,就可以直接查 redis,获取详细的用户信息,减少数据库查询

至于 id,交给前端传递了!

交给前端,是因为大部分业务和用户 id 没有关系,而真正需要用户 id 进行认证鉴权的时候,到时候在专门取出 id 即可!

注销就把 token 给失效掉就 ok 了

过滤器( Filter ):当你有一堆东西的时候,你只希望选择符合你要求的某一些东西。定义这些要求的工具,就是过滤器。

拦截器( Interceptor ):在一个流程正在进行的时候,你希望干预它的进展,甚至终止它进行,这是拦截器做的事情。

监听器( Listener ):当一个事件发生的时候,你希望获得这个事件发生的详细信息,而并不想干预这个事件本身的进程,这就要用到监听器。

spring拦截器的实现方法
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class OldLoginInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("\n-------- OldLoginInterceptor.preHandle --- ");
        System.out.println("Request URL: " + request.getRequestURL());
        System.out.println("Sorry! This URL is no longer used, Redirect to /admin/login");
        response.sendRedirect(request.getContextPath() + "/admin/login");
        return false;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, //
                           Object handler, ModelAndView modelAndView) throws Exception {
        // This code will never be run.
        System.out.println("\n-------- OldLoginInterceptor.postHandle --- ");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, //
                                Object handler, Exception ex) throws Exception {
        // This code will never be run.
        System.out.println("\n-------- QueryStringInterceptor.afterCompletion --- ");
    }
}
JavaServlet自带的过滤器实现
// 要在主启动类中加 @ServletComponentScan 注解进行扫描
@WebFilter(filterName = "MyFilterWithAnnotation", urlPatterns = "/api/*")
public class MyFilterWithAnnotation implements Filter {
   ......
}

# 前后端不分离

一般使用 request.getSession().getAttribute() 操作相关的 session 进行验证

# 参考文章

spring boot 过滤器和拦截器

spring boot 过滤器和拦截器 —Java Guide

# 在 controller 层获取用户信息的四种方法

  1. 前后端使用 session 完成认证,可以增加一个 HttpServletRequest 参数,获得 session,取得 id
  2. 前后端使用 token 完成认证,可以增加一个 HttpServletRequest 参数,解析 token,取得 id
  3. 使用 ThreadLocal,在过滤器 / 拦截器的时候,解析 id,并且放到 ThreadLocal 里面,然后可以在 controller 直接调用
  4. 使用安全框架完成调用,shiro/spring security