SpringBoot——SB整合mybatis案例(残缺版本)第四集(真*大结局)

基础登录功能

要求输入用户名和密码然后从对应的数据库员工表当中查询是否存在对应员工:

查询成功

查看接口文档

 

响应数据中有一个JWT令牌。 

 实现思路

新建一个LoginController用于接收登录请求,然后调用EmpService中的借口进行查询操作。 

三层架构的代码 

 Postman测试

 成功输出

  

控制台成功输出

登录校验

问题引出:

上面的登录功能和其他的页面并没有关联,其他页面还是可以通过url直接访问

在登录成功后把网址复制下来然后退出登录,然后把访问复制的网址应该要重定向到登录页面。

概述:

在服务器端接收到浏览器发送的请求后要先进行校验,校验用户是否已经登录,是则执行业务操作,否则响应错误的结果然后跳转回登录页面。

浏览器和服务器之间的连接通过Http进行,是一种无状态的协议,每次请求都独立,因此无法判断是否已经登录。

实现思路

登录成功后存一个标记,在进行业务操作前先确认标记,失败返回错误信息,前端获取到错误信息自动跳转到登录页面。

 每一个功能块都需要进行如此操作,过于繁琐,因此使用统一拦截的技术,拦截浏览器发来的所有请求并进行校验。

校验过程就是先获取登录标记,校验通过再分发到相应服务。

 

会话技术  

 如上图所示,有三个浏览器和服务器建立起了会话。

服务器会接收多个请求,需要判断是哪些浏览器所发出的,判断的过程就是会话跟踪。

共享数据的例子比如在一个后套管理网站上的登录操作,这是一次独立的请求,但是后面跳转到别的页面时,右上角的个人信息依然存在,这就是多个请求之间的共享数据实现的效果。 

 实现会话跟踪有三种技术:

 Cookie      Session     令牌技术

会话跟踪方案一  ——Cookie

在浏览器第一次登录时可以在服务器创建一个Cookie,服务器会自动将cookie响应给浏览器,然后浏览器会自动存储cookie到本地,此后浏览器每次访问都会自动携带着cookie,服务器会查询是否存在该Cookie 

 

在服务器端设置如上两个方法,在浏览器访问第一个方法,返回的数据的响应头如下图所示,已经把cookie给传回来了。 

 访问c2的资源时的请求头如下图所示,存在刚刚得到的cookie

跨域:目前的开发模式都是前后端分离的开发模式,最终前端程序和后端程序都要分离部署

如下图所示,浏览器访问的前端页面在一个服务器上,在登录会向另一台服务器上发起登录请求,前端和后端的ip地址以及端口号都不一样,可以认为是跨域操作。

出现跨域时不能使用cookie。

 会话跟踪方案二  ——Session

Session是服务器端会话跟踪技术,是存储在服务器端,底层是基于cookie实现。

浏览器第一次访问服务器时会在服务器生成一个拥有id的session,然后将sessionId响应给前端。后续浏览器向服务器的每次请求都会携带sessionId,然后服务器会在众多session中找到是否存在对应的session对象

访问第一个资源 

 下图把两个cookie都传了过去,访问第二个资源

  会话跟踪方案三 ——令牌技术

浏览器成功登录后服务器给浏览器一个令牌,此后浏览器每次访问都带上令牌,服务器校验令牌是否有效,这里可以设定令牌有效期为14天,在14天之内就可以直接登录,不需要输入账号密码。

在同一次会话的多次请求之间共享数据就可以将数据存在令牌里面。

因为不需要一定把令牌存在cookie,所以既支持pc端也支持移动端。 

 

 登录校验——jwt令牌

简介:

jwt就是将原本的json格式数据进行了安全的封装 

应用场景:登录

JWT——生成

引入相关依赖之后可以直接使用jwt提供的工具类jwts进行令牌生成。

令牌生成时需要指定签名算法alg,秘钥secret,还有在jwt令牌中要存储的一些自定义数据。

在官网中给出的一部分签名算法如下。 

使用链式编程实现jwt生成,在测试类中定义如下测试方法。

 @Test
    public void testGenJWT(){
        Map<String ,Object> claims=new HashMap<>();
        claims.put("id",1);
        claims.put("name","yhy");
       String jwt=Jwts.builder()
                .signWith(HS256,"yhy666") //签名算法
                .setClaims(claims)//自定义内容(载荷)
                .setExpiration(new Date(System.currentTimeMillis()+3600*1000))   //设置有效期为1小时
                .compact();   //获取到生成的jwt令牌
        System.out.println(jwt);
    }

上面的setExpiration用于设置过期时间,直接传个new Date()相当于直接过期,所以再设置个3600*1000,前面再加上个时间转换毫秒值,相当于3600秒后过期,也就是一个小时 

运行输出如下jwt

生成的令牌前面两个部分都可以直接使用base64进行阶解码,最后一个部分是进行数字签名之后的结果。 

在官网进行jwt解析。

 在java代码中解析JWT令牌

在test中使用如下代码

  @Test
    public void testParseJwt(){
     Claims claims= Jwts.parser()
                .setSigningKey("yhy666")  //指定签名秘钥
                .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoieWh5IiwiaWQ" +
                        "iOjEsImV4cCI6MTY4MDQwMDExNH0.yOhUB_aHwwoOz9898p" +
                        "B9RSq3SmMXBVqk-eBvLrM8CS0")  //传进jwt令牌
                .getBody();  //获取令牌三个字段中的中间字段
        System.out.println(claims);
    }

控制台输出如下,成功获取中间字段

 篡改令牌中的字符会爆异常,解析过期令牌也会爆异常

 

 JWT——登录后下发令牌

查看接口文档 

 

前端在以后的每一次登录中都会在请求头中携带jwt令牌,后端只需要在请求头中获取jwt令牌用于校验即可。 

 操作流程

 在utils包下

引入如下工具类

package com.example.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 = "yhy666";
    private static Long expire = 43200000L;

    /**
     * 生成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当中

修改如下,增加令牌生成返回。

@RestController
@Slf4j
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);  //将员工id,姓名,用户名都封装到claims中,并装进jwt令牌,有了当前登录的员工信息
            return Result.success(jwt);
        }
        //登录失败,返回错误信息
        return Result.error("同户名或者密码错误");
    }
}

postman测试 成功输出如下

得到的令牌解析后

 前后端联调

登录成功后控制台输出如下 

令牌便自动存储到了浏览器本地

 在后续的每一次请求当中都会将jwt令牌携带在请求头当中。 

 以上就是登录成功后生成jwt令牌的操作

登录校验——统一拦截校验

拦截校验有两种主流的技术方案:

过滤器Filter和拦截器Interceptor

在如下这篇文章中介绍,这里下去直接使用

JavaWeb——过滤器Filter和拦截器Interceptor_北岭山脚鼠鼠的博客-CSDN博客

登录校验Filter

结合上面的jwt令牌和过滤器Filter,可以在过滤器当中进行令牌的有效性校验操作,如果无效则由Filter返回一个错误信息.

在接口文档中

 

 流程分析

 在filter包下新建一个LoginCheckFilter类如下

@Slg4j用于日志记录

@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException  {

        //1.获取请求url。


        //2.判断请求url中是否包含login,如果包含,说明是登录操作,放行。


        //3.获取请求头中的令牌(token)。

        //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)。


        //5.解析token,如果解析失败,返回错误结果(未登录)。
        //6.放行。

    }
}

在Http请求中,请求参数都在HttpServlet,所以要将request先强转为HttpServletRequest,response也要强转。

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.判断令牌是否存在,如果不存在,返回错误结果(未登录)。

这里借助StringUtils工具类的方法hasLength判断字符串是否有长度

没有则说明没有令牌,需要返回规定格式的错误信息。

要响应的是一个json格式的数据,原本在Controller中响应时是可以借助一个Result的类进行实现,但是这里在过滤器当中就只能自己手动转换了

 借助于阿里巴巴提供的fastJSON,

先引入fastJSON的依赖

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

然后直接调用fastJson提供的api将一个对象转成json格式的字符串,然后就可以将该字符串写回响应数据流当中了。

  if(!StringUtils.hasLength(jwt)){
            log.info("请求头token为空,返回未登录的信息");
            Result error = Result.error("NOT_LOGIN");
            //手动转换 对象--json --------> 阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return;
        }

​​​​​​​5.解析token,如果解析失败,返回错误结果(未登录)。

这里如果解析失败了会报异常,所以使用一个try - catch用来捕获异常,返回未登录的错误信息。如果过解析成功就放行。

  try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {//jwt解析失败
            e.printStackTrace();
            log.info("解析令牌失败, 返回未登录错误信息");
            Result error = Result.error("NOT_LOGIN");
            //手动转换 对象--json --------> 阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return;
        }

 6.放行

 log.info("令牌合法, 放行");
        chain.doFilter(request, response);

postman测试 

登录操作测试

fliter直接放行 

查询操作测试 

将登录操作过后得到jwt令牌放到查询部门操作里面的请求头当中进行测试,可以看见后端服务器也成功让查询部门的请求通过了

前端测试

先把浏览器里面的令牌信息清空

 然后刷新页面后会强制跳转登录界面

 登录完成后又会得到一个新的令牌

 服务器控制台输出如下。

 到这里,使用Filter完成拦截校验的操作已经全部结束

拦截器Interceptor

 

相关入门在下面这里,这里直接使用

JavaWeb——过滤器Filter和拦截器Interceptor_北岭山脚鼠鼠的博客-CSDN博客

实现流程和过滤器的流程完全一致 

在定义好的拦截器中的preHandle方法中实现登录校验,并决定返回值

修改后的preHandle方法如下

    @Override //在目标资源方法前运行,返回true:放行,返回false:不放行
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
        //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("请求头token为空,返回未登录的信息");
            Result error = Result.error("NOT_LOGIN");
            //手动转换 对象--json --------> 阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return false;
        }

        //5.解析token,如果解析失败,返回错误结果(未登录)。
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {//jwt解析失败
            e.printStackTrace();
            log.info("解析令牌失败, 返回未登录错误信息");
            Result error = Result.error("NOT_LOGIN");
            //手动转换 对象--json --------> 阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return false;
        }

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

 关闭所有的Filter只剩interceptor后做测试

Postman测试

在查询操作中带上token后成功通过拦截器 

前后端联调 

登录后所有页面的数据都可以正常显示

直接复制页面的地址后退出再访问就会自动跳转到登录页面

 

到这里位置,登录校验拦截器的开发完成。 

异常处理

点击新建部门创建一个已经存在的部门后响应状态码为500,说明服务器端出现问题 

这里报错是出现重复属性,因为建立部门表时为部门名字设置了唯一约束

 

响应的数据不是统一规范的result数据格式 

 

 在三层架构中mapper遇到异常会抛给service,service又会抛会给controller。

解决方案: 

新建一个exception包下GlobalExceptionHandle 类

@RestControllerAdvice //自动将返回的Result数据格式转换成JOSN数据格式
public class GlobalExceptionHandler {
    
    @ExceptionHandler(Exception.class) //表明需要捕获的异常,这里是所有的异常
    public Result ex(Exception ex)
    {
        ex.printStackTrace(); //输出异常的堆栈信息
        return Result.error("杂鱼杂鱼,你操作失败了涅");
    }
}

重启项目测试

继续新建同名部门后页面的错误提示成功显示,响应数据的格式也对了 

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

病毒丨熊猫烧香病毒分析

作者丨黑蛋 一、病毒简介 病毒名称&#xff1a; 熊猫烧香 文件名称&#xff1a; 40fee2a4be91d9d46cc133328ed41a3bdf9099be5084efbc95c8d0535ecee496 文件格式&#xff1a; EXEx86 文件类型(Magic)&#xff1a; MS-DOS executable 文件大小&#xff1a; 29.30KB SHA256&…

【阅读论文】USAD:多变量时间序列上的无监督异常检测

USAD : UnSupervised Anomaly Detection on Multivariate Time Series 摘要 IT系统的自动监控是Orange目前面临的挑战。考虑到其IT运营所达到的规模和复杂性&#xff0c;随着时间的推移&#xff0c;用于推断正常和异常行为的测量所需的传感器数量急剧增加&#xff0c;使得传统…

【C++】内存管理+模板

前言&#xff1a; 本章将详细讲解C内存管理和模板的实现。 第一部分我们讲解C内存管理&#xff0c;C语言中有malloc/calloc/realloc等开辟空间和free释放空间&#xff0c;那么C将符合实现呢&#xff1f; 第二部分我们会一起来初步认识模板与泛型编程&#xff0c;并详细探讨函…

微服务高级篇【1】之微服务保护

文章目录前言一 初识Sentinel1.1 雪崩问题1.2 解决方法1.3 小结1.4 服务保护技术对比1.5 Sentinel介绍1.6 Sentinel安装1.7 微服务整合Sentinel二 测试工具&#xff1a;Jmeter2.1 Jmeter安装和配置2.2 Jmeter快速入门2.2.1 设置中文语言2.2.2 设置Jmeter桌面快捷图标2.3 Jmeter…

已经提了离职,还有一周就走,公司突然把我移出企业微信,没法考勤打卡, 还要继续上班吗?...

黎明前的黑暗最容易出事&#xff0c;离职前的几天也最容易出幺蛾子&#xff0c;比如下面这位网友的遭遇&#xff1a;已经提了离职&#xff0c;还有一周就正式离职了&#xff0c;公司突然把我移出企业微信&#xff0c;没法考勤打卡了&#xff0c; 还要继续上班吗&#xff1f;该怎…

BGP小型实验

实验分析 1.主要考察的是对BGP配置的熟练 2.实验需要在R1与R5分别发布一条路由可以在BGP 中使用network 网段 掩码命令 3.R1与R2,R4与R5是EBGP&#xff0c;而R2,R3,R4是IBGP 实验操作 1.配置接口ip,与环回路由 以R1为例 2.AS内部需要实现非直连的建立是需要保证IBGP内部是通的所…

蓝桥杯30天真题冲刺|题解报告|第三十天

大家好&#xff0c;我是snippet&#xff0c;今天是我们这次蓝桥省赛前一起刷题的最后一天了&#xff0c;今天打了一场力扣周赛&#xff0c;前面3个题都是有思路的&#xff0c;第三个题只过了一半的案例&#xff0c;后面看完大佬们的题解彻悟&#xff0c;下面是我今天的题解 目录…

蓝桥杯备考

数论&#xff1a;判断素数&#xff0c;鸽笼定理&#xff0c;抽屉理论 注意事项&#xff1a; long类型的数后面要加L long s 2658417853L; 保留几位小数&#xff1a; System.out.printf(“%.2f”, arg); 四舍五入问题&#xff1a;比如保留两位小数&#xff0c;就在数的后面再…

java基础知识汇总

目录 1、Java基础语法 1、类型转换问题 1. 运算符 1.1 算术运算符&#xff08;理解&#xff09; 1.2 赋值运算符&#xff08;应用&#xff09; 1.3 自增自减运算符&#xff08;理解&#xff09; 1.4 关系运算符&#xff08;应用&#xff09; 1.5 逻辑运算符&#xff08…

【CSS】清除浮动 ④ ( 清除浮动 - 使用双伪元素清除浮动 | 代码示例 )

文章目录一、清除浮动 - 使用双伪元素清除浮动二、代码示例一、清除浮动 - 使用双伪元素清除浮动 为 .clearfix:before 和 .clearfix:after 并集选择器 , 设置如下样式 : /* 清除浮动 - 使用双伪元素清除浮动 */.clearfix:before,.clearfix:after {content: "";displ…

ERTEC200P-2 PROFINET设备完全开发手册(1)

本教程为ERTEC200P-2的基础开发教程&#xff0c;可以掌握PN设备开发的基本流程。虽然没有涉及PN协议的详细解析&#xff0c;但是希望根据本文档多多练习&#xff0c;熟能生巧&#xff0c;逐渐能够掌握PN设备开发。 &#xff08;注意&#xff1a;本手册基于西门子DEVKIT V47协议…

oracle导入的表中文名称乱码无法删除导致删除用户也失败

由于一开始弄数据库的时候忘记设置编码格式&#xff0c; 导致导入dmp文件之后带中文的表名变成了乱码 然后plsql右键删除表显示表不存在 一开始的时候寻思备份下表结构跟表数据 直接删除用户完事了 删除用户报递归遍历错误 寻思重装这个数据库太过于耗时 不值当的 就是看那几…

【JWT鉴权】如何来写一个token令牌认证登录?

目录一. &#x1f981; 话题引入1.2 什么是JWT&#xff1f;二. &#x1f981; 技术体现2.1 引入依赖2.2 编写JWT工具类2.3 编写登录方法2.4 编写JWT拦截器验证令牌2.5 编写要配置拦截的接口三. &#x1f981; 话题终结一. &#x1f981; 话题引入 在做项目过程中&#xff0c;我…

【halcon】为啥匹配到ROI外面去了?

背景 匹配到ROI外面去了 中心恰好在roi有效区域内&#xff01;&#xff08;粉色是ROI区域&#xff09; 网上查到的资料&#xff01; PaintRegion改变外部环境 //HOperatorSet.ReduceDomain(image, ho_ProductRegionAll, out imgReduced); //替换为&#xff1a; HObject all…

Web前端 HTML、CSS

HTML与CSSHTML、CSS思维导图一、HTML1.1、HTML基础文本标签1.2、图片、音频、视频标签1.3、超链接、表格标签1.4、布局1.5、表单标签1.6、表单项标签综合使用1.7、HTML小结二、CSS&#xff08;简介&#xff09;2.1、引入方式2.2、选择器2.3、CSS属性Web前端开发总览 Html&…

Linux基础操作 常用命令 Centos

Linux 1.Linux的引言 Linux是一套免费使用和自由传播的类Unix操作系统&#xff0c;是一个基于POSIX和Unix的多用户、多任务、支持多线程和多CPU的操作系统。伴随着互联网的发展&#xff0c;Linux得到了来自全世界软件爱好者、组织、公司的支持。它除了在服务器操作系统方面保持…

面试题-学习网络协议必备:七层模型与协议之间的映射关系

一、概念 OSI七层模型是计算机网络中的一种标准化分类和描述方式&#xff0c;它将网络协议划分为不同的层次&#xff0c;每个层次负责不同的功能。这种模型被广泛应用于网络设计、开发和维护&#xff0c;以便于不同系统之间的互操作性和相互通信。 二、各层介绍 第一层&#x…

vue项目代理配置大全

1.vite &#xff08;vue3-admin-element-template-master&#xff09; server: {host: 0.0.0.0, //服务器ip地址 port: 5566, //本地端口fs: {strict: false, // 支持引用除入口目录的文件},open: true, // 是否自动在浏览器打开proxy: {/pcapi: {target: https:....../pcapi…

老板想要可视化大屏?零代码开发3D可视化大屏,只要10分钟

上周末和供应链管理的朋友一起喝茶&#xff0c;我吐槽着我做报表的繁琐&#xff0c; 他用很疑惑的眼神看着我说&#xff1a;这事不是在模板上改改数据就行了吗&#xff1f;我连忙逼他给我分享了这个香饽饽。 回到家&#xff0c;我直接开始研究起了可视化大屏&#xff0c;后悔自…