权限管理框架Shiro源码浅析
一. Shiro概述
1.1 shiro是什么
Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.
- shiro是一个强大并且容易使用的执行认证、授权、加密、session管理的Java安全框架。
- 使用shiro的API,你可以快速容易的保护你的任何应用(从最小的移动应用到最大的web企业级应用)。
二. Authentication认证
2.1 executeLogin
- AuthenticatingFilter中的executeLogin方法
- 调用createToken方法创建一个token令牌,大致步骤:
- 通过request获取用户名和密码。
- 通过isRememberMe和getHost方法分别获得是否记住我(布尔值true/false)和主机。
- 最后通过以上四个参数,创建UsernamePasswordToken对象。
- 判断令牌Token是否为null,如果为空,抛出异常。
- 通过getSubject方法获得主体subject(用户或第三方应用)。
- 实际是通过SecurityUtils类中的getSubject方法来获取主体的。
- 调用subject对象中的login方法。
- 认证成功,调用onLoginSuccess方法。
- 重定位到登录成功页面
- 认证失败,调用onLoginFailure方法。
- 返回登录页面
2.2 login
- DelegatingSubject.login
- 拿到DefaultSecurityManager的login方法返回值subject(登录成功后新创建的主体)
- 如果subject是DelegatingSubject类型那么就将其造型为DelegatingSubject,并取出principals用户名和host主机分别赋值给principals和host,否则就直接从subject中取出principals赋值给principals。
- 将authenticated赋值为true,认证成功。
- 拿到subject中的session
- DefaultSecurityManager.login
- 通过调用authenticate方法及之后的许多方法得到认证信息info(doAuthenticate)
- 创建新的subject
2.3 doAuthenticate
- ModularRealmAuthenticator中的doAuthenticate
- 首先判断是否配置了realm,没有配置抛异常
- 获取所有的realm
- 如果realm只有一个那么调用doSingleRealmAuthentication方法,否则调用doMultiRealmAuthentication
2.4 doSingleRealmAuthentication
- ModularRealmAuthenticator中的doSingleRealmAuthentication方法
- 判断该realm是否支持UsernamePasswordToken,如果不支持抛异常
- 调用getAuthenticationInfo方法获得认证信息info
- 如果认证信息info为null,就抛出未知账户异常UnknownAccountException,否则返回info
2.5 getAuthenticationInfo
- AuthenticatingRealm中的getAuthenticationInfo
- getAuthenticationInfo方法中最重要的是调用我们自定义的Realm对象,比如CustomeRealm,返回info
- 从token中取出用户名
- 通过用户名从数据库中查出对应的用户信息
- 从用户信息中取出密码,根据密码、用户名、类名创建SimpleAuthenticationInfo认证信息,并返回到getAuthenticationInfo方法中。
- 之后匹配密码是否符合,调用assertCredentialsMatch方法
- 通过getCredentialsMatcher方法获得凭证匹配器
- 凭证匹配器调用doCredentialsMatch方法,判断密码是否一致
- 从令牌token和info各自取出密码
- 通过equals方法来判断密码是否一致,一致返回true,反之,返回false
- doCredentialsMatch方法返回false,那么就抛出错误凭证异常IncorrectCredentialsException。
- 返回到getAuthenticationInfo中,将info返回
- getAuthenticationInfo方法中最重要的是调用我们自定义的Realm对象,比如CustomeRealm,返回info
三. Authorization授权
3.1 checkPermission
- AuthorizingSecurityManager中的checkPermission方法
- 直接调用ModularRealmAuthorizer中的checkPermission方法
- ModularRealmAuthorizer中的checkPermission方法
- 首先判断是否配置了realm,没有配置抛异常
- 执行ModularRealmAuthorizer中的isPermitted方法,如果返回false,那么就抛未认证异常UnauthorizedException。
3.2 isPermitted
- ModularRealmAuthorizer中的isPermitted方法
- 首先判断是否配置了realm,没有配置抛异常
- 判断realm是否实现了Authorizer接口,如果没有继续下一次循环去取出下一个realm
- 调用AuthorizingRealm中的isPermitted方法
- AuthorizingRealm中的isPermitted方法
- 将字符串permission转换为Permission对象p
- 然后继续调用该类中isPermitted方法
- 调用getAuthorizationInfo方法获得授权信息
- 继续调用该类中isPermitted方法
- 调用getPermissions的到该用户的所有权限
- 将该用户的所有权限依次取出调用implies方法,跟访问模块所需的permission权限进行比较,如果包含该权限则返回true,否则进行下一次的循环,直到用户拥有的所有权限全部比较完毕,如果依然没有匹配的,那么就返回false,说明该用户并没有访问该模块的权限。
3.3 getAuthorizationInfo
- getAuthorizationInfo方法中的一部分代码
- 通过doGetAuthorizationInfo方法获得授权信息info。
- 然后将info返回。
3.4 doGetAuthorizationInfo
- CustomRealm中的doGetAuthorizationInfo方法
- 获取用户名
- 根据用户名查询出该用户所有的权限。
- 将每个权限对象中的权限码取出放入权限码集合中。
- 创建简单授权信息对象,并且将权限码集合设置进去。
- 将授权信息返回。
3.4 getPermissions
- AuthorizingRealm中的getPermissions方法
- 判断授权信息是否为空
- 调用resolvePermissions方法,将info中的字符串权限集合转换为对象权限集合,并返回
3.5 resolvePermissions
- AuthorizingRealm中的resolvePermissions方法
- 创建一个空的集合
- 获取权限解析器
- 依次取出stringPermission(privilegeCode),调用resolvePermission方法返回WidcardPermission权限对象,最后放入集合中。
- 将集合返回。
3.6 resolvePermission
- WildcardPermissionResolver中的resolvePermission方法
- 该方法直接创建了一个WidcardPermission权限对象,并返回。
3.7 implies
- WildcardPermission中的implies方法
- otherParts是访问该模块所需要的权限,part是用户目前拥有的一种权限
- 该方法举例子最容易明白
- 假设otherParts集合中的数据是user、list,part集合中的数据是user、modify
- 第一次进行比较,user==user返回true,取反,进行下一次循环
- 第二次比较,list != modify返回false,取反,返回false,结束方法。