Web-登录功能实现(含JWT令牌)

登录功能

这个登陆功能先不返回JWT令牌
在这里插入图片描述
登陆会返回JWT令牌
一会在登陆验证时讲解JWT令牌(返回的data就是它)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

登录校验

概述

就是你比如复制一个url
用一个未曾登陆对应url系统的浏览器访问
他会先进入登陆页面
登陆校验就是实现这个功能
简而言之,就是不能让你直接访问内部数据,要先登陆才可以

首先http协议是无状态的
每次请求都是独立的
而我们浏览器和web服务器之间就是http协议
在这里插入图片描述
实现思路
存一个登陆标记
在这里插入图片描述
每个请求前有if判断对应队列标记
登陆就正常执行,没有登陆就去登陆界面
但是:这样太繁琐了
在这里插入图片描述
所以我们使用统一拦截来做
在这里插入图片描述

对应技术主要介绍登陆标记(会话技术)和统一拦截呗
在这里插入图片描述

会话技术

在这里插入图片描述
简而言之会话跟踪技术就是保证同一浏览器多个http请求之间能够数据共享
一个会话可包含多个请求(这些请求之间数据共享)
共享数据作用
比如你更新验证码是一次请求,然后它是不是返回了结果
然后你输入验证码登录,又是一次请求
他需要验证你输入和上一次请求返回的数据是否相同
so:需要请求之间的数据共享
在这里插入图片描述
会话跟踪就是保证数据共享的核心技术
有三种实现方式
1.客户端Cookie
2.服务器端session
3.令牌技术(最常用)

会话跟踪技术-Cookie

cookie
优点:http协议支持的
当你进行请求的时候,如果服务端设置cookie
会自动将cookie以及里面数据返回给浏览器-响应头
然后浏览器会自动存储该cookie
下次请求的时候会携带该cookie进行访问-请求头
缺点:移动端和ios端不支持
不安全:因为数据存储在浏览器,不能涉及一些隐私数据
cookie不能跨域:因为现在一般是前后端分离部署
你要访问前端页面,返回给你的cookie是不能在访问后端时候用的(或者说是不识别的)
在这里插入图片描述

先访问c1
再访问c2
看看能不能进行数据的同步
在这里插入图片描述
如果响应头有set-cookie这个,浏览器会自动存储起来(因为我们设置了cookie,这是http协议支持的)
在这里插入图片描述
存储在这里
在这里插入图片描述
现在我们访问c2,会发现请求头带着set-cookie(我们原来接收到的cookie)来进行访问
在这里插入图片描述

会话跟踪技术-session

底层:
session底层用cookie实现的
当浏览器请求服务端,服务端建立一个session,每一个session有自己的id
然后服务器端响应数据的时候会将session的id通过cookie响应给浏览器
然后浏览器存储该session的id,每次请求带着这个session的id进行请求
相当于session的id把set-Cookie在响应头和请求头的位置占了
只不过换到了服务器端存储
就可以通过session对象来实现请求数据间的共享
优点就不说了
缺点:底层是cookie的方式,所以cookie的缺点有
而且现在一般是部署多台服务器,如果你第一次请求服务器转载给了1号服务器
而第二次转请求带着对应id到了2号服务器,他就识别不到对应的session,即使识别到了也是错误的

在这里插入图片描述

还是测试一下
s1和s2两个请求,一个session
在这里插入图片描述
s1请求,响应头有session的id,然后浏览器对应的session的ID存储起来
在这里插入图片描述
s2请求,带着session的id到请求头的set-cookie进行请求,且可以获取到s1的数据
在这里插入图片描述

会话跟踪技术-令牌

这种就是请求生成令牌
令牌再反给浏览器,浏览器存储令牌==(令牌存储在浏览器(客户端)中)==
(可以存储在cookie或者别的储存空间)
浏览器带着令牌进行访问再通过拦截判断令牌有效性
想要共享数据把对应数据存储在令牌中即可

1.由于可以存储在任何存储空间不止set-cookie
所以就可以在pc端移动端都可以用
2.服务器端不需要存储数据,就是服务器端是集群(分布式)
也可以使用,且不存储数据在服务器端,减轻服务器端压力
3.不用担心令牌伪造问题,具体在令牌技术讲解

在这里插入图片描述
下面讲常用令牌技术JWT令牌

JWT令牌技术

介绍

三部分之间用.隔开
前两部分是json形式存储后经过Base64编码成的字符串
最后一部分签名是自动生成的根据前两部分(比如第一部分签名算法)并且加入指定秘钥(不是编码)
第一部分是令牌类型和签名算法
第二部分是我们的自定义信息和一些默认信息
在这里插入图片描述
应用场景:登录认证
在这里插入图片描述
就两部操作其实生成令牌和校验令牌

生成令牌和校验令牌

导入依赖,使用对应的工具类Jwts
setExpiration()是设置有效期,单位是毫秒
signWith()设置签名算法和秘钥
setClaim()是设置自定义数据即json

在这里插入图片描述
在这里插入图片描述

对应的生成
在这里插入图片描述
编码当然可以阶码,可以复制该数据到JWT官网即可解码(或者Base64解码工具)解码前两部分
上面是签名算法,你用什么算法生成的选择什么就行
在这里插入图片描述
如何基于Java代码校验JWT令牌
指定签名秘钥和对应字符串即可解析(当然令牌过期、秘钥不对或者字符串不对会报错)
so只要没有报错就是校验成功
在这里插入图片描述
结果如图
在这里插入图片描述

下发和生成JWT令牌功能实现

看开发文档
返回JWT令牌
还说明了对应请求头名(这个是前端做的)
我们只需要生成对应令牌返回给前端(Result里面的data)

在这里插入图片描述
引入一个JWT工具类(自己定义的)
两个方法
1.生成令牌2.校验令牌
指定两个属性

private static String signKey="ieheima";//秘钥是itheima
private static Long exprie=43200000L;//过期时间12个小时

在这里插入图片描述
只需要改controller(因为只有controller是真正接收对应请求和客户端互动的)
controller
发送正确username和password会返回JWT令牌
在这里插入图片描述
前后端联调一下
登录完后的响应令牌

在这里插入图片描述
前端将对应的数据存储在浏览器本地存储空间Local Storage
在这里插入图片描述
然后下次请求就会带着这个JWT令牌
如图一个新请求也会携带该令牌
在这里插入图片描述

统一拦截,校验令牌

就是对应的拦截器了
这里就写两个目前企业常用的
一个Filter一个Interceptor

过滤器Filter

快速入门

就是先过滤一遍
然后从数据库取到数据还能再过滤一遍
就是要经过两次Filter
在这里插入图片描述

快速入门
1.定义一个类实现Filter接口和对应方法,上面写上注解@WebFilter(“”)来表明要拦截上面请求
这里的话是/*就是所有请求都拦截
2.在启动类上加上注解@ServletComponentScan
因为Filter属于JavaWeb组件不是SpringBoot提供的
在这里插入图片描述
注意:是java.servlet包里的Filter接口
接口中就三个方法,初始化,拦截,和销毁
初始化和销毁是有默认实现的,因为不常用
这里就全实现了
在这里插入图片描述

拦截请求中需要进行放行操作
放行需要调用参数chain里面的doFilter()方法,里面参数就是请求和响应

chain.doFilter(request,response);

详解

在这里插入图片描述

Filter执行流程
经过两次Filter,但其实一次请求对应那个方法只会生效一次
就是你放行完执行完对应的controller
会直接到那个方法的下一步操作
也就是放行后逻辑
在这里插入图片描述
拦截路径
注解WebFilter对应的urlPattern属性赋值
在这里插入图片描述
没什么好说的
过滤器链
其实这个doFilter参数就有一个过滤器链
就是需要经过多个过滤器
如果还有过滤器就会放行给下一个过滤器
如果没有就会到放行到web资源
在这里插入图片描述
如果不指定过滤器顺序,默认按首字母排序
在这里插入图片描述

Filter实现登录校验

在这里插入图片描述
在这里插入图片描述
对应的代码逻辑,稍微有点复杂
可以看看
这里因为要返回一个json的数据格式
需要用到albb的fastJson依赖,记得添加

@Slf4j
//@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        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.判断令牌是否存在,如果不存在,返回错误结果(未登录)。
        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 {
            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);

    }
}

在这里插入图片描述

拦截器Interceptor

快速入门

在这里插入图片描述
1.定义一个类实现拦截器接口HandlerInterceptor,实现对应方法
2.配置拦截器,定义一个类实现WebMVCConfigurer,且用@Configuration注解
表示是配置类
重写addInterceptor方法注册拦截器到我们之前实现的类
和指定对应拦截路径

在这里插入图片描述
注意:
preHandle的controller执行前的操作
postHandle是controller执行后的操作
afterCompletion是渲染完最后的操作

详解

拦截路径
在这里插入图片描述
根据对应需求在配置文件注册拦截器的时候进行拦截路径的配置
既能指定对于拦截路径,也能指定不拦截的路径

/*只能匹配一级路径,而/**可以匹配任意级路径
如/emp/*能匹配/emp/1但不能匹配/emp/1/2,而emp/*无论后面有多少级路径都可以匹配

执行流程
先过滤器再拦截器
在这里插入图片描述

拦截器实现登录校验

实现思路是一样的
在这里插入图片描述
对应的拦截逻辑

@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    @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;
    }

    @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...");
    }
}

当然我们还有对应的配置类进行拦截器注册
在这里插入图片描述

异常处理

有时出现异常
比如你添加一个相同名称的部门
但是部门的名称是用unique修饰的,你的sql语句就会报错
此时返回的错误信息不是Result
而是一个别的json
在这里插入图片描述
在这里插入图片描述
对应的异常抛出到controller层
最开始想到的也是在controller每个方法进行try catch操作
但是太繁琐
所以就出现了全局异常处理器

全局异常处理器

在这里插入图片描述
@RestControllerAdvice
以用来修饰类,表示该类为一个全局异常处理类
@ExpectionHandler()后面指定捕获哪一类的异常
Exception就是捕获所有异常
定义完后再进行对应操作
就是标准的Result结果
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小袁拒绝摆烂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值