【shiro】shiro整合JWT——3.执行流程

前言

shiro整合JWT系列,主要记录核心思路–如何在shiro+redis整合JWTToken。
上一篇中,主要讲如何在shiro框架中配置Jwt,以及token执行的流程。
该篇主要梳理整个代码的执行流程。
ps:本文主要以记录核心思路为主,以下都是个人理解,有问题请各位大佬指导一下

根据token的流程,请求主要分为2个情况:

  1. 登录获取token
  2. 携带token请求

1、登录获取token

在shiro+redis+jwt中,Controller层的登录接口不用执行subject.login(token)方法(没有用到Realm的doCredentialsMatch验证,可以参照【Shiro】SimpleAuthenticationInfo如何验证password),而是在Controller层手写了校验方法。

  • 逻辑如下:
	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public xxx返回类 login(@RequestBody SysUser user) throws Exception{
	
	// 1、获取前端请求的 用户名和密码
	
	// 2、校验用户是否有效
		2.1 根据用户名查询数据库,得到用户数据库信息BDUser(用户名,加密密码,盐值...2.2 判断有效性(用户是否存在,是否注销,是否被删除)

	// 3、校验用户名或密码是否正确
		3.1 使用(前端传来的用户名,密码,DBUser.盐值)进行加密计算,得到加密密码
		3.2 判断是否正确,加密密码.equals(DBUser.getPassWord())

	// 4、生成token,并将token存入redis缓存中
	
	// 5、设置xxx返回类对象(主要把token返回去)
	
	}

从上面逻辑可以看出,这个登录接口主要就给前端返回了一个token,并将其存入redis中
这个登录接口,并没有真正进行登录操作subject.login(token),因此也没执行到Realm中的认证。

2、携带token请求

这里主要以流程介绍为主,通过简单的源码说明携带token请求的调用过程。通过【shiro】shiro整合JWT——1.需要创建的类 中的JwtFilter,我们可以看到,主要重写了preHandleisAccessAllowedexecuteLoginonAccessDenied这4个方法(该例子为简单实现)。

在JwtFilter中,这4个方法主要的执行顺序是
preHandle -> isAccessAllowed -> executeLogin -> onAccessDenied

2.1 preHandle

携带token的请求,先会被ShiroConfig中的filterChainDefinitionMap.put("/**", "jwt");给拦截,然后执行到我们定义的JwtFilter中;在JwtFilter中,会根据Filter中具体的doFilter方法实现(这里不细说,后续在细讲)。

/**
     * 对跨域提供支持
     * 过滤器链中拦截请求,判断是否为跨域请求
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        // 判断请求方式是否为跨域请求
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setHeader("Access-Control-Allow-Origin", httpServletRequest.getHeader("Origin"));
            httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
            httpServletResponse.setHeader("Access-Control-Allow-Methods", httpServletRequest.getHeader("Access-Control-Request-Method"));
            httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
            return false;
        }
        // 继续执行过滤器链
        return super.preHandle(request, response);
    }

preHandle方法在最后return了super.preHandle(request, response);,点进去查看,可以发现它重写PathMatchingFilter中的preHandle,然后再回调回了PathMatchingFilter中的preHandle。(看上去有点绕,看下图)
在这里插入图片描述

总结:感觉就是PathMatchingFilter类是主线流程,(重写的)preHandle方法B就是在preHandle方法A中加入我们自定义的内容,然后再继续执行preHandle方法A原有的内容。

顺着源码继续往下梳理流程,先看preHandle的代码,发现执行了isFilterChainContinued方法,里面又执行了onPreHandle方法。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 疑惑:这个重写的是哪个方法?
    回答:我们顺着JwtFilter的继承方法网上查找,如下图;可以发现,是由AccessControlFilter类重写的。在这里插入图片描述
    点进去AccessControlFilter类可以看到我们重写的方法isAccessAllowedonAccessDenied,中间的关系是具有 先后顺序的或。
    在这里插入图片描述

2.2 isAccessAllowed

	/**
     * 执行登录认证
     *
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        try {
            executeLogin(request, response);
            return true;
        } catch (Exception e) {
            throw new AuthenticationException("Token失效请重新登录");
        }
    }

里面执行了executeLogin方法

2.3 executeLogin

	/**
     * 执行登录
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader("ACCESS_TOKEN");

        JwtToken jwtToken = new JwtToken(token);
        // 提交给realm进行登入,如果错误他会抛出异常并被捕获 
        // 这里真正实现shiro的登录,getSubject(request, response)直接SecurityUtils.getSubject()也一样
        getSubject(request, response).login(jwtToken);
        // 如果没有抛出异常则代表登入成功,返回true
        return true;
    }

对于getSubject(request, response).login(jwtToken);有疑惑的可以看下【shiro】subject.login(token)流程源码分析

PS:以防万一,遗忘却又没有看,这里根据总结图做下提示,执行getSubject(request, response).login(jwtToken)方法后,就会执行到Realm的认证方法。

2.4 onAccessDenied

根据源码,我们知道isAccessAllowed执行返回为True的话,就不会执行onAccessDenied(也就意味着登录成功,走向realm的两个方法);如果执行返回为False的话,就会执行onAccessDenied,用来处理登录失败的情况。

	/**
     * 如果没有登录,直接返回401未授权提示
     *
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        // 这里是个坑,如果不设置的接受的访问源,那么前端都会报跨域错误,因为这里还没到corsConfig里面
        httpResponse.setHeader("Access-Control-Allow-Origin", ((HttpServletRequest) request).getHeader("Origin"));
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setCharacterEncoding("UTF-8");
        httpResponse.setContentType("application/json; charset=utf-8");
		httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
        try {
            // 返回到前端信息
            httpResponse.getWriter().write("未登录或登录失效,请重新登录!");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值