登录(JWT令牌、Filter过滤器、interceptor拦截器)

登录校验
-会话技术
-JWT令牌
-过滤器Filter
-拦截器Interceptor
会话
浏览器与服务器的一次连接就叫会话
登录就是一次请求,就是建立的会话
会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
比如你登陆以后能看到很多登录后的信息
客户端会话跟踪技术:cookie
服务端会话跟踪技术:Session
令牌技术(当前企业最主流的技术)
cookie存储当前的用户名id存储在浏览器本地自动携带到服务端
服务器会自动的将cookie响应给浏览器。浏览器接收到响应的数据以后会自动的将cookie存储在浏览器本地。在后续的请求当中浏览器会自动将cookie携带到服务器端

cookie
优点:HTTP协议中支持的技术
缺点:
移动端APP无法使用Cookie
不安全,用户可以自己禁用cookie
Cookie不能跨域

session
优点:存储在服务端,安全
缺点:
服务器集群环境下无法直接使用Session
Cookie的缺点

令牌
优点:
支持PC端、移动端
解决集群环境下的认证问题
减轻服务器端存储压力
缺点:需要自己实现

令牌:字符串(用户身份的标识)
在客户端存储令牌

客户端将每一次请求都将令牌携带在身边,每一次服务器接受请求都会同意拦截。验证令牌的真伪

JWT令牌
header头 -- 载荷(自定义部分)-- 数字签名
这个令牌就是base64格式的解码
引入依赖

<dependency>
<groupld>io.jsonwebtoken</groupld>
<artifactld>jjwt</artifactld>
<version>0.9.1</version>
</dependency>

创建在utilsjwt令牌的工具类两个方法一个是生成令牌,一个是解析令牌(校验令牌)-

指定签名算法,然后是一个密钥:字符串
.设置自定义数据(载荷)map集合写入数据put
.设置有效期
.拿到字符串类型的返回值也就是jwt令牌

public class JwtUtils {
  private static String signKey = "itheima";
  private static Long expire = 43200000L;

public static String generateJwt (Map<String, Object> claims){

  String jwt=Jwts.builder()                        //构建令牌
    .setClaims(claims)                             //自定义内容(载荷)
    .signWith(SignatureAlgorithm.HS256,signKey)    //签名算法                          
    .setExpiration(new Date(System.currentTimeMillis() + expire))//没置有效期为1h
    .compact();
    System.out.println(jwt);
    return jwt;
  }
}

parse:解析

JWT解码

public static Claims parseJwT(String jwt){
    Claims claims = Jwts.parser()
        .setsigningKey(signKey)
        .parseClaimsJws(jwt)
        .getBody();
    System.out.println(claims)  
    return claims;
  }
}

JWT是非常安全可靠的

过滤器Filter
概念: Filter 过滤器,是JavaWeb 三大组件(Servlet、Filter、Listener)之一
过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能
过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等。

Filter快速入门(定义类、配置:两个注解)
1、定义Filter:定义一个类,实现 Filter 接口,并重写其所有方法
2、配置Filter: Filter类上加 @WebFilter 注解,配置拦截资源的路径。引导类上加 @ServletComponentScan 开启Servlet组件支持

//首先标记为过滤器注解,还要在spap那个主键类启动类上面加注解那个@servletscan
//首先的filter接口是包javax.servlet
@WebFilter(urlPatterns = "/* ")

public class DemoFilter implements Filter {
    public void init(EilterConfig filterConfig) throws ServletException{ //初始化方法,Web服务器启动,创建Filter时调用,只调用一次
        Filter.super.init(filterConfig);
  }
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){  //拦截到请求时,调用该方法。可调用多次
        System.out.println("拦截方法执行,拦截到了请求...");
        chain.doFilter(request, response);
}
    public void destroy(){  //销毁服务器、服务器关闭时调用,只调用一次
        Filter.super.destroy();
}
}

过滤器链
一个web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链
浏览器 - > 请求 - > filter1 - 》 filter2 - > web资源
f2就只实现dofilter即可
拦截是1完了放行后,再拦截2再放行是2之后是首先是2放行后的逻辑然后才是1
这个过滤器的顺序是按照类名的自然顺序排名的

登录校验jwt流程
获取请求url。
判断请求url中是否包含login,如果包含,说明是登录操作,放行
获取请求头中的令牌 (token)
判断令牌是否存在,如果不存在,返回错误结果(未登录)
解析token,如果解析失败,返回错误结果 (未登录)
放行。

字符串转换工具

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>

关于登录拦截器LoginCheckFilter.class

@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoqinCheckFilter implements Filte{
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException{
          HttpServletRequest req = (HttpServletRequest) request;      //获取请求数据
          HttpServletResponse resp = (HttpServletResponse) response;  //获取响应数据
          
          //1.获取请求url。
          String url = req.getRequestURL().tostring();
          log.info("请求的url:{}",url);
      
          //2.判断请求url中是否包含Login,如果包含,说明是登录操作,直接放行
          if(url.contains("login")){
              log.info("登求操作,放行...");
              chain.doFilter(request,response);
              return;
          }

          //3.获取请求头中的令牌 (token)
          String jwt = req.getHeader("token");

          //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
          //使用spring提供的工具类StringUtils
          //haslength这个字符串是否有长度
          if(!StringUtils.hasLength(jwt)){
              log.info("请求头token为空,返回未登录的信息");
              Result error = Result.error("NOT LOGIN");

              //手动转换对象--json ---->阿里巴巴fastJSON
              String notLogin = JSONObject.toJSONString(error);

              //getWriter获取输出流用write直接响应给浏览器
              resp.getWriter().write(notLogin);
              return;
          }
          
        //5.如果令牌存在,解析token,如果解析失败,返回错误结果(未登录)
        try{

            JwtUtils.parseJWT(jwt); 

        }catch (Exception e){//jwt解析失败
      
            e.printStackTrace();
            log.info("解析令牌失败,返回未登录错误信息");
            Result error = Result.error("NOT_LOGIN");    //文档要求返回的提示msg

            //手动转换 对象--json -----> 阿集巴巴fastJSON  
            String notLogin  JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return;
          }
        //6.放行。
        log.info("令牌合法,放行");
        chain.doFilter(request, response);
           

拦截器interceptor
用来动态拦截控制器方法的执行
类似过滤器,spring内定的。在指定的方法调用前后,根据业务需要执行预先设定的代码

快速入门拦截器
1.定义拦截器,实现Handlerlnterceptor接口,并重写其所有方法。需要将这个类放入ioc容器 @component
2.注册拦截器

创建拦截器interceptor实现handler接口重写三个方法
单独创建个包存放配置类,存放注册拦截器
调用addinterceptor来注册拦截器,然后指定拦截什么的资源。
在过滤器中拦截所有资源配置是/*,但是在拦截器中拦截所有资源需要配置/**

详解(拦截路径、执行流程)

指定拦截哪些资源,也可以指定不拦截哪些资源

拦截路径 含义 举例
/* 一级路径 能匹配/depts,/emps,/login,不能匹配 /depts/1
/** 任意级路径 能匹配/depts,/depts/1,/depts/1/2
/depts/* /depts下的一级路径 能匹配/depts/1,不能匹配/depts/1/2,/depts
/depts/** /depts下的任意级路径 能匹配/depts,/depts/1,/depts/1/2,不能匹配/emps/1

//注册拦截器
@Configuration //配置类
public class WebConfig implements WebMvcConfigurer{
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry){
          //拦截路径的配置
          registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");    //需要拦截/**不需要拦截/login
  }
}

tomcat是servlet容器是不识别controller容器的,
拦截器的执行流程,过滤器先执行拦截,在过滤器执行了放行操作之后才放行给拦截器

接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现Handlerlnterceptor接口。
拦截范围不同:过滤器Filter会拦截所有的资源,而interceptor只会拦截Spring环境中的资源

//定义拦截器
拦截器的步骤和登录校验的过滤器逻辑是完全一样的、而登录校验的逻辑应当定义在prehandle第一个方法当中
只需要将过滤器的校验登录流程全部复制到prehandle中即可,但不用前两行的格式强制转换,因为参数中传过来的即是servlet格式
多余的return true删除,记得修改形参名和逻辑步骤参数名一致
第二步骤是否包含login那直接 return true就直接放行了第四、五步骤不存在直接false
最后一步不需要dofilter直接return true即可




@Component
public class LoginCheckInterceptor implements HandlerInterceptor{
     @override //目标资源方法执行前执行,放回true: 放行,返回false: 不放行
     public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception{
          System.out.printIn("preHandle ...");  
          return true;
     }
  
    


全局异常处理器
比如说添加相同的信息会报错

@RestControllerAdvice
public class GlobalExceptionHandler{
  
    @ExceptionHandler(Exception.class)      //代表捕获所有的异常
    public Result ex(Exception ex){
          ex.printStackTrace();
          return Result.error(”对不起操作失败,请联系管理员“);
  }
}
posted @ 2024-01-29 08:39  launch  阅读(129)  评论(0编辑  收藏  举报