使用token完成Shrio鉴权认证
ShiJh Lv3

用于前后端分离情况,需要使用token进行权限验证

前后端分离——使用token完成Shrio鉴权认证

核心流程

  1. 用户登录,保存token到数据库,并返回给前端
  2. 用户访问资源并携带token,经过各种过滤器
  3. 因为没有执行过Subject.login()所以访问限制资源是会被拦截失效
  4. 失败后调用自定义FilteronAccessDenied(),获取携带的token并执行登录(executeLogin)
  5. 登陆时使用自定义的Realm验证token合法性和时效性,通过则返回认证信息
  6. Shiro调用CredentialsMatcher验证输入的credentials与Realm中返回的认证信息是否匹配(需要自定义Mathcer)
  7. 有了认证信息即可进行授权鉴权!!!

原理解析

使用token进行登录认证,主要就是要求前端的请求需要合法token支持,所以这个token是前后端共享的,因此需要JWT进行加密,而设计Shiro认证时需要考虑到认证信息由默认的Username/Password转变成了自定义的Token,因此不能再使用原有的过滤器来控制认证鉴权,所以实现一个Filter是完成这个任务的一个至关重要的操作。

自定义Filter实现原理

以不需要RememberMe为例,我们需要实现的 Filter Interface 为AuthenticatingFilter,实现几个关键的方法:

  1. AuthenticationToken createToken(request,response)

    这个方法将会在认证失败后尝试登录时调用(executeLogin),它返回的AuthenticationToken将作为Subject.login方法的参数,也就是**它将进入到我们自定义的Realm当中!**根据需求,此处创建AuthenticationToken 我们需要依赖于前端传递的token,因此我们是从request中获得的(请求头、请求体)

    无法获取怎么办呢?直接返回null即可,Shiro将会抛出IllegalStateException

  2. boolean onAccessDenied(request,response)

    在请求被Filter验证失败时会调用此方法,该方法的返回值决定这个请求是否能够放行,在这个方法中我们才会执行登录操作!我们根据登录是否成功(Realm中返回认证信息)来决定是否放行,如何登录?就采用刚才所说的executeLogin方法,这是AuthenticatingFilter类已经实现了的方法,根据createToken返回的Token进行登录,如果登录成功即可返回true

自定义Realm实现原理

使用Token方法进行验证的Realm其实与Username/Password的核心思想是相同的,只不过认证时通过前端的token从数据库中取出完整的token实体,对token本身的信息进行校验如是否失效等作为认证是否通过的依据。

授权时,我们需要从Token实体中获取到用户对应的ID,通过ID从数据库中查找角色、权限进行然后正常的进行授权操作!

自定义Token

这里要说的Token分为:

  1. 前端能够获取到的token——String

    这个理应为一个加密后的字符串,也是认证流程中的核心。

  2. Shiro进行认证的token—— AuthenticationToken

    实际上就是把String类型的token封装成实现接口的对象,其token作为Principal也作为Credentials

  3. 保存于数据库中的token——JavaBean

    一个实体Entity类型的对象,与数据库设计有关,加密过的token应是其主要部分,此外,该token对应的用户与之是一对一关系,所以也可以保存对应用户的ID

理清了各种Token,接下来要解决的就是Token如何加密的问题了,就如前面提到的,Token可以使用JWT,使用JWT可以方便的设置失效时间,节省数据库资源,也提供secret加密,能够进行二次验证,这里不再赘述使用方法。

自定义CredentialsMatcher

@Override
public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {}

Token是从前端获取的Token,Info是Realm返回的Info,在该方法中实现验证即可。

😄自定义Matcher后需要注入到自定义的Realm中,通过setCredentialsMatcher方法

 评论