当前位置:   article > 正文

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

tlias智能辅助系统

目录

一、登录功能

(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. <!--JWT令牌-->
  2. <dependency>
  3. <groupId>io.jsonwebtoken</groupId>
  4. <artifactId>jjwt</artifactId>
  5. <version>0.9.1</version>
  6. </dependency>
② 生成令牌

  1. // 测试jwt令牌生成
  2. @Test
  3. public void testGenJwt()
  4. {
  5. Map<String,Object> 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<String, Object> 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<String,Object> 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. <!--fastJSON-->
  2. <dependency>
  3. <groupId>com.alibaba</groupId>
  4. <artifactId>fastjson</artifactId>
  5. <version>1.2.76</version>
  6. </dependency>
  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. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/729494
推荐阅读
相关标签
  

闽ICP备14008679号