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

news2024/11/15 7:35:32

目录

一、登录功能

(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层

@Slf4j
@RestController
public class LoginController {

    @Autowired
    private EmpService empService;

    @PostMapping("/login")
    public Result login(@RequestBody Emp emp)
    {
        log.info("员工登录:{}",emp);

        Emp e = empService.login(emp);

        return e != null? Result.success():Result.error("用户名或密码错误!");

    }
}

(2)Service层

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

    //员工登录
    Emp login(Emp emp);
    //员工登录
    @Override
    public Emp login(Emp emp) {

         return empmapper.getByUsernameAndPassword(emp);
    }

(3)Mapper层 

    //员工登录
    @Select("select * from emp where username = #{username} and password = #{password}")
    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令牌依赖
        <!--JWT令牌-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
② 生成令牌

	// 测试jwt令牌生成
	@Test
	public void testGenJwt()
	{
		Map<String,Object> claims = new HashMap<>();
		claims.put("id",1);
		claims.put("name","Tom");

		String jwt = Jwts.builder()
				.signWith(SignatureAlgorithm.HS256,"itheima")  //签名算法
				.setClaims(claims) //自定义内容
				.setExpiration(new Date(System.currentTimeMillis()+3600*1000)) //设置有效期为1h
				.compact();

		System.out.println(jwt);
	}

 生成令牌

eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVG9tIiwiaWQiOjEsImV4cCI6MTY5ODA2OTAxNX0.hjkFgUORQs1DOnaZa4A2n_5jDVLxkLO89i3Ri5D7LqA

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

JSON Web Tokens - jwt.io

 

③ 解析令牌
	//解析jwt令牌
	@Test
	public void testParseJwt()
	{
		Claims claims = Jwts.parser()
				.setSigningKey("itheima") //密钥
				.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVG9tIiwiaWQiOjEsImV4cCI6MTY5ODA2OTAxNX0.hjkFgUORQs1DOnaZa4A2n_5jDVLxkLO89i3Ri5D7LqA")
				.getBody();
		System.out.println(claims);
	}

 

(3)登录后下发令牌

① 引入JWT令牌操作工具类

package com.itroye.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 = "itheima";
    private static Long expire = 43200000L; //规定过期时间为12h

    /**
     * 生成JWT令牌
     * @param claims JWT第二部分负载 payload 中存储的内容
     * @return
     */
    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;
    }

    /**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }
}
② LoginController 生成令牌
@Slf4j
@RestController
public class LoginController {

    @Autowired
    private EmpService empService;

    @PostMapping("/login")
    public Result login(@RequestBody Emp emp)
    {
        log.info("员工登录:{}",emp);

        Emp e = empService.login(emp);

        //登录成功,生成令牌,下发令牌
        if(e != null)
        {
            Map<String,Object> claims = new HashMap<>();
            claims.put("id",e.getId());
            claims.put("name",e.getName());
            claims.put("username",e.getUsername());

            String jwt = JwtUtils.generateJwt(claims); //jwt令牌包含当前登录的员工信息

            return Result.success(jwt);
        }

        //登录失败,返回错误信息
        return Result.error("用户名或密码错误!");

    }
}

4、Filter过滤器

(1)过滤器快速入门

step 1:新建filter包+类

step 2:过滤器代码编写

注意是:选javax.servlet这个!

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

@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {

    @Override //初始化的方法 只调用一次
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("初始化方法执行了");
    }

    @Override //拦截到请求之后的方法,调用多次
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("拦截到了请求!");

        filterChain.doFilter(servletRequest,servletResponse); // 放行
    }

    @Override //销毁的方法 只使用一次
    public void destroy() {
        System.out.println("销毁方法执行了");
    }
}

其中注解@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依赖

        <!--fastJSON-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
@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;

        //1、获取请求url
        String url = req.getRequestURL().toString();
        log.info("请求url为:{}",url);

        //2、判断url是否包含login,如果包含则放行
        if(url.contains("login"))
        {
            log.info("登录操作,放行");
            filterChain.doFilter(servletRequest,servletResponse);
            return;
        }

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

        //4、判断令牌是否有效
        if(!StringUtils.hasLength(jwt))
        {
            log.info("请求头为空,返回为登录信息");
            Result error = Result.error("NOT_LOGIN");
            // 手动转换成JSON  --阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            //上面的流程:将错误信息封装在Result中-->转换为json格式-->response的write()方法将错误信息返回给浏览器
            return;
        }

        //5、解析token,如果解析失败,返回错误结果(未登录)
        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;
        }

        //6、放行
        log.info("令牌合法,放行!");
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

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

5、Iterceptor拦截器

(1)拦截器快速入门

step 1:新建interceptor包+类

step 2:拦截器代码编写

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

① 定义拦截器
@Component //交给ioc容器管理
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override // 目标资源方法运行【前】运行 true:放行 false:拦截
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle运行了");
        return true;
    }

    @Override // 目标资源方法运行【后】运行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle运行了");
    }

    @Override // 视图渲染完毕后运行 最后运行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion运行了");
    }
}
② 配置拦截器 
  • addPathPatterns("/**") ———— 拦截
  • excludePathPatterns("/login") ———— 不拦截

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

@Configuration  //配置类
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
    }
}

(2)拦截器执行流程

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

(3)拦截路径

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

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

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

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

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

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

@Slf4j
@Component //交给ioc容器管理
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override // 目标资源方法运行【前】运行 true:放行 false:拦截
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
        System.out.println("preHandle运行了");

        //1、获取请求url
        String url = req.getRequestURL().toString();
        log.info("请求url为:{}",url);

        //2、判断url是否包含login,如果包含则放行
        if(url.contains("login"))
        {
            log.info("登录操作,放行");
            return true;
        }

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

        //4、判断令牌是否有效
        if(!StringUtils.hasLength(jwt))
        {
            log.info("请求头为空,返回为登录信息");
            Result error = Result.error("NOT_LOGIN");
            // 手动转换成JSON  --阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            //上面的流程:将错误信息封装在Result中-->转换为json格式-->response的write()方法将错误信息返回给浏览器
            return false;
        }

        //5、解析token,如果解析失败,返回错误结果(未登录)
        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 false;
        }

        //6、放行
        log.info("令牌合法,放行!");
        return true;
    }

    @Override // 目标资源方法运行【后】运行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle运行了");
    }

    @Override // 视图渲染完毕后运行 最后运行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion运行了");
    }
}

三、全局异常处理器

step1:连包带类创建异常类

step2:编写代码

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class) //定义捕获所有类型的异常
    public Result ex(Exception ex)
    {
        ex.printStackTrace();
        return Result.error("对不起,操作失败,请联系管理员");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1144385.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

kaggle新赛:AI Village夺旗赛挑战

赛题名称&#xff1a;AI Village Capture the Flag DEFCON31 赛题链接&#xff1a;https://www.kaggle.com/competitions/ai-village-capture-the-flag-defcon31 赛题背景 夺旗赛这款广受欢迎的竞技游戏&#xff0c;不仅可以在户外进行。数字夺旗赛指的是一系列需要参赛者利…

python操作MySQL,SQL注入问题,视图,触发器,事务,存储过程,内置函数,流程控制,索引

一、python操作MySQL 导入第三方模块&#xff1a;pymysql 操作步骤&#xff08;文字描述&#xff09;&#xff1a; 1. 先链接MySQL host&#xff0c;port&#xff0c;username&#xff0c;password&#xff0c;charset&#xff0c;库&#xff0c;autocommit等 2. 在python中书…

06条件判断

if语句的基本语法 if关键字后面跟一个判断条件 如果条件成立那么就运行判断条件里面的代码 else处理条件不满足时候的代码块 m 9 if m > 10:print("买一瓶醋") else:print("钱不够&#xff0c;请带够钱再来吧&#xff01;")#条件判断流程图 进入网…

stream流—关于Collectors.toMap使用详解

目录 使用规则&#xff1a;1.将list转成以id为key的map&#xff0c;value是id对应的某对象2.假如id存在重复值&#xff0c;则会报错Duplicate key xxx3.想获得一个id和name对应的Map<String, String>3.1 name为空时null3.2 id重复时 4.分组 使用groupingby 使用规则&…

系列二十五、@Configuration的作用及解析原理

一、作用 Configuration是用来代替传统的xml的配置方式配置bean的。 二、不加Configuration注解不能配置bean吗 能。 三、加与不加的区别 3.1、区别 加了Configuration注解&#xff0c;会为配置类创建cglib动态代理&#xff0c;Bean方法的调用就会通过容器getBean进行获取…

面试准备中........

一、Linux 计算机网络相关&#xff1a; 1.OSI七层模型 应用层 &#xff1a;给用户提供操作界面 表示层&#xff1a;数据的表示&#xff1a;将字符转化为2进制或将2进制转化为字符。加密&#xff1a;对称加密和非对称加密&#xff0c;ssh协议。压缩&#xff1a;将文件压缩。…

C# 图解教程 第5版 —— 第12章 枚举

文章目录 12.1 枚举12.1.1 设置底层类型和显式值12.1.2 隐式成员编号 12.2 位标志12.2.1 Flags 特性12.2.2 使用位标志的示例&#xff08;*&#xff09; 12.3 关于枚举的更多内容 12.1 枚举 枚举是值类型。只有一种类型的成员&#xff1a;命名的整数值常量。 每个枚举成员都被…

VS工程的“多dll与exe文件合并”

运行环境 ILMerge插件 1、打开 VS的“工具 - NuGet包管理器 - 管理解决方案的NuGet程序包” 2、在浏览中搜索“ILMerge”&#xff0c;在官方源中&#xff0c;3.0.41版本的插件已不支持使用了 3、下拉列表其他版本可以安装&#xff0c;使用3.0.40 4、下载封装好的“ILMerge”任…

Kotlin基础——变量、函数、字符串模板、类

变量 Kotlin和Java一样是静态语言&#xff0c;所有表达式类型在编译期已经确定&#xff0c;public为默认可见性 变量由 var/val变量名[: 数据类型][?][ 值] 组成&#xff0c;如 var a 1var b: Int b 3var s: String? nullval language arrayListOf("java")…

Kmeans算法的K值选择技巧【Elbow Method + Silhouette Score Method】

文章目录 一、方法简述二、使用到的数据集三、代码实现四、结论 一、方法简述 在Kmeans算法中最终聚类数量K的选择主要通过两个方法综合判断&#xff1a; Elbow Method 这是一种绘制k值范围的平方和的方法。如果此图看起来像一只手臂&#xff0c;则k是选择的类似肘部的值。从这…

走进人工智能的大门:打造职业梦想的契机

在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;不再是科幻小说的情节&#xff0c;而是我们现实生活和职业生涯中的现实部分。从自动驾驶汽车到智能助手&#xff0c;AI 的影响已经渗透到各个领域。而中国&#xff0c;作为全球人工智能市场的重要一员&#xff…

HTML简单实现v-if与v-for与v-model

Vue启动&#xff01;&#xff01; 首先VIewModel将View和Model连接一起&#xff0c;Model的数据改变View的数据也变 使用Visual Studio Code 启动Vue需要vue.js插件和导入CDN(包) vue.js插件&#xff1a;CTRL shift x 在搜索栏搜 索vue.js安装即可 CDN&#xff1a; http…

利用HTTP2,新型DDoS攻击峰值破纪录

亚马逊、Cloudflare 和谷歌周二联合发布消息称&#xff0c;一种依赖于 HTTP/2 快速重置技术的攻击行为对它们造成了破纪录的分布式拒绝服务 (DDoS) 攻击。 根据披露的信息&#xff0c;该攻击自8月下旬以来便一直存在&#xff0c;所利用的漏洞被跟踪为CVE-2023-44487&#xff0c…

花园这样去装,让户外空间更上一个层次

花园这样去装&#xff0c;让户外空间更上一个层次 园林设计师马修-布罗姆利&#xff08;Matthew Bromley&#xff09;说&#xff1a;”我深受法国和英国花园传统的影响&#xff0c;而这些客户想要的是一个能让人回想起在法国南部迷人的凉棚下度过的时光的空间。斑驳的树荫、燃烧…

关于Web应用和容器的指纹收集以及自动化软件的制作

一次对Web应用的渗透&#xff0c;九成都是从信息收集开始&#xff0c;所以信息收集就显得尤为重要。关键信息的收集可以使你在后期渗透的时候更加的得心应手&#xff0c;把渗透比喻成走黑暗迷宫的话&#xff0c;那信息收集可以帮你点亮迷宫的大部分地图。 信息收集涉及的点特别…

[Leetcode] 0094. 二叉树的中序遍历

94. 二叉树的中序遍历 题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3,2]示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[]示例 3&#xff1a; 输入&…

现代风格设计,大美至简!福州中宅装饰,福州装修

风格&#xff1a;现代 面积&#xff1a;70m 户型&#xff1a;住宅 现代风格设计是较为流行的风格 注重家居空间的布局与使用功能的结合 追求时尚和潮流 客厅空间 简约风格已经成为装修的一种流行趋势 讲究以简洁的视觉制造出简单的风格 客厅的空间整体户型 是纵向客餐厅…

【Python】一个句子中也许有多个连续空格,过滤掉多余的空格,只留下一个空格

题目要求&#xff1a;一个句子中也许有多个连续空格&#xff0c;过滤掉多余的空格&#xff0c;只留下一个空格 例&#xff1a;&#xff08;为了方便观看&#xff0c;以 ▢ 代替空格&#xff09; 输入&#xff1a;123▢▢abc▢▢▢python 输出&#xff1a;123▢abc▢python 参考…

vue源码分析(五)——vue render 函数的使用

文章目录 前言一、render函数1、render函数是什么&#xff1f; 二、render 源码分析1.执行initRender方法2.vm._c 和 vm.$createElement 调用 createElement 方法详解&#xff08;1&#xff09;区别&#xff08;2&#xff09;代码 3、原型上的_render方法&#xff08;1&#xf…

轻量级仿 Spring Boot=嵌入式 Tomcat+Spring MVC

啥&#xff1f;Spring Boot 不用&#xff1f;——对。就只是使用 Spring MVC Embedded Tomcat&#xff0c;而不用 Boot。为啥&#xff1f;——因为 Boot 太重了&#xff1a;&#xff09; 那是反智吗&#xff1f;Spring Boot 好好的就只是因为太重就不用&#xff1f;——稍安勿…