• 【javaweb】学习日记Day12 - tlias智能管理系统 - 登录校验 JWT令牌 过滤器 拦截器 全局异常处理


    目录

    一、登录功能

    (1)Controller层

    (2)Service层

    (3)Mapper层 

    二、登录校验

    1、会话技术概述

    2、会话跟踪方案

    (1)Cookie

    (2)Session —— 基于Cookie

    (3)令牌技术

    3、JWT令牌

     (1)简介

    (2)生成及校验

    ① 引入JWT令牌依赖

    ② 生成令牌

    ③ 解析令牌

    (3)登录后下发令牌

    ① 引入JWT令牌操作工具类

    ② LoginController 生成令牌

    4、Filter过滤器

    (1)过滤器快速入门

    (2)过滤器执行流程

    (3)拦截路径

    (4)过滤器链

    (5)案例——用Filter实现登录校验

    ① 手动转换JSON——fastJSON

    5、Iterceptor拦截器

    (1)拦截器快速入门

    ① 定义拦截器

    ② 配置拦截器 

    (2)拦截器执行流程

    (3)拦截路径

    (4)过滤器与拦截器的区别

    (5)案例——用Interceptor实现登录校验 

    三、全局异常处理器


    一、登录功能

    接口信息

    请求参数

    响应数据

    (1)Controller层

    1. @Slf4j
    2. @RestController
    3. public class LoginController {
    4. @Autowired
    5. private EmpService empService;
    6. @PostMapping("/login")
    7. public Result login(@RequestBody Emp emp)
    8. {
    9. log.info("员工登录:{}",emp);
    10. Emp e = empService.login(emp);
    11. return e != null? Result.success():Result.error("用户名或密码错误!");
    12. }
    13. }

    (2)Service层

    由于是校对员工用户名和密码,所以用empservice层和empmapper层就好

    1. //员工登录
    2. Emp login(Emp emp);
    1. //员工登录
    2. @Override
    3. public Emp login(Emp emp) {
    4. return empmapper.getByUsernameAndPassword(emp);
    5. }

    (3)Mapper层 

    1. //员工登录
    2. @Select("select * from emp where username = #{username} and password = #{password}")
    3. Emp getByUsernameAndPassword(Emp emp);

    二、登录校验

    1、会话技术概述

    • 会话:一次会话中包含多次请求响应
    • 会话跟踪:服务器需要识别多次请求是否来自同一浏览器,以便在同一次会话的多次请求中共享数据

    2、会话跟踪方案

    (1)Cookie

    • 优点:HTTP协议支持的技术
    • 缺点:
      • 移动端APP不支持
      • 不安全,用户可以禁用Cookie
      • Cookie不可以跨域

    (2)Session —— 基于Cookie

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

    (3)令牌技术

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

    3、JWT令牌

     (1)简介

    • 概念:JWT就是将json格式的数据以安全的形式封装
    • 组成:
      • 第一部分——头:记录令牌类型、签名算法等。【eg:"alg":"HS256","type":"Tom"】
      • 第二部分——有效载荷:携带一些自定义信息、默认信息。【eg:"id":"1"】
      • 第三部分——签名:防止Token被篡改,保证安全性。将头、有效载荷并加入指定密钥,提高指定签名算法计算得来。

    (2)生成及校验

    ① 引入JWT令牌依赖
    1. <dependency>
    2. <groupId>io.jsonwebtokengroupId>
    3. <artifactId>jjwtartifactId>
    4. <version>0.9.1version>
    5. dependency>
    ② 生成令牌

    1. // 测试jwt令牌生成
    2. @Test
    3. public void testGenJwt()
    4. {
    5. Map claims = new HashMap<>();
    6. claims.put("id",1);
    7. claims.put("name","Tom");
    8. String jwt = Jwts.builder()
    9. .signWith(SignatureAlgorithm.HS256,"itheima") //签名算法
    10. .setClaims(claims) //自定义内容
    11. .setExpiration(new Date(System.currentTimeMillis()+3600*1000)) //设置有效期为1h
    12. .compact();
    13. System.out.println(jwt);
    14. }

     生成令牌

    eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVG9tIiwiaWQiOjEsImV4cCI6MTY5ODA2OTAxNX0.hjkFgUORQs1DOnaZa4A2n_5jDVLxkLO89i3Ri5D7LqA

    在官网可以查到令牌的原数据

    JSON Web Tokens - jwt.io

     

    ③ 解析令牌
    1. //解析jwt令牌
    2. @Test
    3. public void testParseJwt()
    4. {
    5. Claims claims = Jwts.parser()
    6. .setSigningKey("itheima") //密钥
    7. .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVG9tIiwiaWQiOjEsImV4cCI6MTY5ODA2OTAxNX0.hjkFgUORQs1DOnaZa4A2n_5jDVLxkLO89i3Ri5D7LqA")
    8. .getBody();
    9. System.out.println(claims);
    10. }

     

    (3)登录后下发令牌

    ① 引入JWT令牌操作工具类

    1. package com.itroye.utils;
    2. import io.jsonwebtoken.Claims;
    3. import io.jsonwebtoken.Jwts;
    4. import io.jsonwebtoken.SignatureAlgorithm;
    5. import java.util.Date;
    6. import java.util.Map;
    7. public class JwtUtils {
    8. private static String signKey = "itheima";
    9. private static Long expire = 43200000L; //规定过期时间为12h
    10. /**
    11. * 生成JWT令牌
    12. * @param claims JWT第二部分负载 payload 中存储的内容
    13. * @return
    14. */
    15. public static String generateJwt(Map claims){
    16. String jwt = Jwts.builder()
    17. .addClaims(claims)
    18. .signWith(SignatureAlgorithm.HS256, signKey)
    19. .setExpiration(new Date(System.currentTimeMillis() + expire))
    20. .compact();
    21. return jwt;
    22. }
    23. /**
    24. * 解析JWT令牌
    25. * @param jwt JWT令牌
    26. * @return JWT第二部分负载 payload 中存储的内容
    27. */
    28. public static Claims parseJWT(String jwt){
    29. Claims claims = Jwts.parser()
    30. .setSigningKey(signKey)
    31. .parseClaimsJws(jwt)
    32. .getBody();
    33. return claims;
    34. }
    35. }
    ② LoginController 生成令牌
    1. @Slf4j
    2. @RestController
    3. public class LoginController {
    4. @Autowired
    5. private EmpService empService;
    6. @PostMapping("/login")
    7. public Result login(@RequestBody Emp emp)
    8. {
    9. log.info("员工登录:{}",emp);
    10. Emp e = empService.login(emp);
    11. //登录成功,生成令牌,下发令牌
    12. if(e != null)
    13. {
    14. Map claims = new HashMap<>();
    15. claims.put("id",e.getId());
    16. claims.put("name",e.getName());
    17. claims.put("username",e.getUsername());
    18. String jwt = JwtUtils.generateJwt(claims); //jwt令牌包含当前登录的员工信息
    19. return Result.success(jwt);
    20. }
    21. //登录失败,返回错误信息
    22. return Result.error("用户名或密码错误!");
    23. }
    24. }

    4、Filter过滤器

    (1)过滤器快速入门

    step 1:新建filter包+类

    step 2:过滤器代码编写

    注意是:选javax.servlet这个!

    选中接口名并alt+enter自动生成接口中的三个方法

    1. @WebFilter(urlPatterns = "/*")
    2. public class DemoFilter implements Filter {
    3. @Override //初始化的方法 只调用一次
    4. public void init(FilterConfig filterConfig) throws ServletException {
    5. System.out.println("初始化方法执行了");
    6. }
    7. @Override //拦截到请求之后的方法,调用多次
    8. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    9. System.out.println("拦截到了请求!");
    10. filterChain.doFilter(servletRequest,servletResponse); // 放行
    11. }
    12. @Override //销毁的方法 只使用一次
    13. public void destroy() {
    14. System.out.println("销毁方法执行了");
    15. }
    16. }

    其中注解@WebFilter(urlPatterns = "/*") 是拦截路径,而要使用它,需要在启动类上添加

    @ServletComponentScan //开启对Servlet组件的支持

    (2)过滤器执行流程

    浏览器发出请求——过滤器执行放行前的逻辑——放行——login、depts(访问web资源)——放行之后的逻辑——响应给浏览器

    (3)拦截路径

    拦截路径url值含义
     拦截具体路径 /login只有访问/login路径,才会被拦截
     目录拦截 /emps/*访问/emps下所有资源时,都会被拦截
     拦截所有 /*访问所有资源都会被拦截

    (4)过滤器链

    可以设置多个过滤器,形成过滤器链

    • 过滤器的执行先后顺序是根据过滤器类名(字符串)的自然排序,也就是说AbcFilter在DemoFilter过滤器之前
    • 第一个过滤器放行后进入第二个过滤器

    (5)案例——用Filter实现登录校验

    • 登录成功后,生成一个jwt令牌并返回给前端,前端将该jwt令牌记录(在请求头token中携带令牌),并在后续每一次请求中携带到服务端
    • 过滤器校验jwt令牌的有效性,进行拦截放行操作

    ① 手动转换JSON——fastJSON

    注意:之前在Controller里直接返回Result.success(),因为在注释@RestController下,会自动将返回值转换为json格式,而在过滤器类中,没有该注解,因此需要手动转换成json格式

    这里我们用到阿里巴巴提供的fastJSON

    首先在pom.xml文件中引入fastJSON依赖

    1. com.alibaba
    2. fastjson
    3. 1.2.76
    1. @Slf4j
    2. @WebFilter(urlPatterns = "/*")
    3. public class LoginCheckFilter implements Filter {
    4. @Override
    5. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    6. HttpServletRequest req = (HttpServletRequest) servletRequest;
    7. HttpServletResponse resp = (HttpServletResponse) servletResponse;
    8. //1、获取请求url
    9. String url = req.getRequestURL().toString();
    10. log.info("请求url为:{}",url);
    11. //2、判断url是否包含login,如果包含则放行
    12. if(url.contains("login"))
    13. {
    14. log.info("登录操作,放行");
    15. filterChain.doFilter(servletRequest,servletResponse);
    16. return;
    17. }
    18. //3、获取请求头中的令牌(token)
    19. String jwt = req.getHeader("token");
    20. //4、判断令牌是否有效
    21. if(!StringUtils.hasLength(jwt))
    22. {
    23. log.info("请求头为空,返回为登录信息");
    24. Result error = Result.error("NOT_LOGIN");
    25. // 手动转换成JSON --阿里巴巴fastJSON
    26. String notLogin = JSONObject.toJSONString(error);
    27. resp.getWriter().write(notLogin);
    28. //上面的流程:将错误信息封装在Result中-->转换为json格式-->response的write()方法将错误信息返回给浏览器
    29. return;
    30. }
    31. //5、解析token,如果解析失败,返回错误结果(未登录)
    32. try{
    33. JwtUtils.parseJWT(jwt); //解析报错 说明令牌错误
    34. }catch (Exception e)
    35. {
    36. e.printStackTrace();
    37. log.info("解析令牌失败,返回错误信息");
    38. // 返回错误信息丝滑小连招
    39. Result error = Result.error("NOT_LOGIN");
    40. String notLogin = JSONObject.toJSONString(error);
    41. resp.getWriter().write(notLogin);
    42. return;
    43. }
    44. //6、放行
    45. log.info("令牌合法,放行!");
    46. filterChain.doFilter(servletRequest,servletResponse);
    47. }
    48. }

           此时,当我们在网页访问depts、emps时,如果没有登录,页面会强制跳转到登录页面,只有合法登录,获取到合法令牌,才能访问后台

    5、Iterceptor拦截器

    (1)拦截器快速入门

    step 1:新建interceptor包+类

    step 2:拦截器代码编写

    ctrl+o调出面板,选中三个默认方法

    ① 定义拦截器
    1. @Component //交给ioc容器管理
    2. public class LoginCheckInterceptor implements HandlerInterceptor {
    3. @Override // 目标资源方法运行【前】运行 true:放行 false:拦截
    4. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    5. System.out.println("preHandle运行了");
    6. return true;
    7. }
    8. @Override // 目标资源方法运行【后】运行
    9. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    10. System.out.println("postHandle运行了");
    11. }
    12. @Override // 视图渲染完毕后运行 最后运行
    13. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    14. System.out.println("afterCompletion运行了");
    15. }
    16. }
    ② 配置拦截器 
    • addPathPatterns("/**") ———— 拦截
    • excludePathPatterns("/login") ———— 不拦截

    首先连包带类创建一个配置类

    1. @Configuration //配置类
    2. public class WebConfig implements WebMvcConfigurer {
    3. @Autowired
    4. private LoginCheckInterceptor loginCheckInterceptor;
    5. @Override
    6. public void addInterceptors(InterceptorRegistry registry) {
    7. registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
    8. }
    9. }

    (2)拦截器执行流程

    先执行过滤器,然后再执行拦截器 

    (3)拦截路径

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

    可以匹配/depts/1,不能匹配/depts/1/2

    /depts/**/depts下的任意路径不能匹配/emps/1

    (4)过滤器与拦截器的区别

    • 过滤器会拦截所有资源
    • 拦截器只会拦截spring环境的资源 

    (5)案例——用Interceptor实现登录校验 

    1. @Slf4j
    2. @Component //交给ioc容器管理
    3. public class LoginCheckInterceptor implements HandlerInterceptor {
    4. @Override // 目标资源方法运行【前】运行 true:放行 false:拦截
    5. public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
    6. System.out.println("preHandle运行了");
    7. //1、获取请求url
    8. String url = req.getRequestURL().toString();
    9. log.info("请求url为:{}",url);
    10. //2、判断url是否包含login,如果包含则放行
    11. if(url.contains("login"))
    12. {
    13. log.info("登录操作,放行");
    14. return true;
    15. }
    16. //3、获取请求头中的令牌(token)
    17. String jwt = req.getHeader("token");
    18. //4、判断令牌是否有效
    19. if(!StringUtils.hasLength(jwt))
    20. {
    21. log.info("请求头为空,返回为登录信息");
    22. Result error = Result.error("NOT_LOGIN");
    23. // 手动转换成JSON --阿里巴巴fastJSON
    24. String notLogin = JSONObject.toJSONString(error);
    25. resp.getWriter().write(notLogin);
    26. //上面的流程:将错误信息封装在Result中-->转换为json格式-->response的write()方法将错误信息返回给浏览器
    27. return false;
    28. }
    29. //5、解析token,如果解析失败,返回错误结果(未登录)
    30. try{
    31. JwtUtils.parseJWT(jwt); //解析报错 说明令牌错误
    32. }catch (Exception e)
    33. {
    34. e.printStackTrace();
    35. log.info("解析令牌失败,返回错误信息");
    36. // 返回错误信息丝滑小连招
    37. Result error = Result.error("NOT_LOGIN");
    38. String notLogin = JSONObject.toJSONString(error);
    39. resp.getWriter().write(notLogin);
    40. return false;
    41. }
    42. //6、放行
    43. log.info("令牌合法,放行!");
    44. return true;
    45. }
    46. @Override // 目标资源方法运行【后】运行
    47. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    48. System.out.println("postHandle运行了");
    49. }
    50. @Override // 视图渲染完毕后运行 最后运行
    51. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    52. System.out.println("afterCompletion运行了");
    53. }
    54. }

    三、全局异常处理器

    step1:连包带类创建异常类

    step2:编写代码

    1. @RestControllerAdvice
    2. public class GlobalExceptionHandler {
    3. @ExceptionHandler(Exception.class) //定义捕获所有类型的异常
    4. public Result ex(Exception ex)
    5. {
    6. ex.printStackTrace();
    7. return Result.error("对不起,操作失败,请联系管理员");
    8. }
    9. }
  • 相关阅读:
    国庆节难忘回家路:趣事、风景、反思
    Github2024-04-25 开源项目日报Top10
    自大型人格分析,如何改变自大型性格?
    js修改函数this指向的三种常用方法总结
    【多线程进阶】CAS实现及应用
    下载安装升讯威在线客服系统时提示风险的解决办法
    Maven——采用profile设定不同的开发环境切换
    怎么判断自己是否适合转行软件测试
    关于ElasticSearch版本7.8.0进行排坑
    01-06-Hbase基础定义
  • 原文地址:https://blog.csdn.net/weixin_61639349/article/details/133711575