(二期)13、权限框架shiro讲解
- 用户认证 - 用户身份识别,即登录
- 用户授权 - 访问控制
- 密码加密 - 加密敏感数据防止被偷窥
- 会话管理 - 与用户相关的时间敏感的状态信息
Subject:
Subject currentUser = SecurityUtils.getSubject();
SecurityManager:
Realms
- 记住一点,Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。
Subject:
SecurityManager:
Realm:
Subject及Realm,分别是主体及验证主体的数据源。
Session:
SessionManager:
SessionDAO:
CacheManager:
Cryptography:
The SecurityManager
- Authentication
- Authorization
- Session Management
- Cache Management
- Realm coordination
- Event propagation
- “Remember Me”
- Services
- Subject creation
- Logout
- and more.
#获取当前用户
Subject currentUser = SecurityUtils.getSubject();
#判断用户已经认证
currentUser.isAuthenticated()
#用户登录
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
#记住我
token.setRememberMe(true); currentUser.login(token);
#判断是否有角色权限
currentUser.hasRole("schwartz")
#判断是否有资源操作权限
currentUser.isPermitted("lightsaber:wield")
#登出
currentUser.logout();
- 注解判断是否有权限,通过spring aop的形式完成
//属于user角色
@RequiresRoles("user")
//必须同时属于user和admin角色
@RequiresRoles({"user","admin"})
//属于user或者admin之一;修改logical为OR 即可
@RequiresRoles(value={"user","admin"},logical=Logical.OR)
//符合index:hello权限要求
@RequiresPermissions("index:hello")
//必须同时复核index:hello和index:world权限要求
@RequiresPermissions({"index:hello","index:world"})
//符合index:hello或index:world权限要求即可
@RequiresPermissions(value={"index:hello","index:world"},logical=Logical.OR)
@RequiresAuthentication
@RequiresUser
@RequiresGusst
- 1、首先调用Subject.login(token)进行登录,其会自动委托给Security Manager,调用之前必须通过SecurityUtils. setSecurityManager()设置;
- 2、SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;
- 3、Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;
- 4、Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;
- 5、Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。
常见异常
- DisabledAccountException(禁用的帐号)
- LockedAccountException(锁定的帐号)
- UnknownAccountException(错误的帐号)
- ExcessiveAttemptsException(登录失败次数过多)
- IncorrectCredentialsException (错误的凭证)
- ExpiredCredentialsException(过期的凭证)
认证流程自定义的功能
- Realms -
- JdbcRealm
- IniRealm
- 自定义Realm,编写用户登录认证过程
- SessionDAO -
- 把session信息存到redis、memcache等缓存中间件中
- 其中,session的id可以自定义生成格式:属性:sessionIdGenerator
- AuthenticationToken -
- 默认方式UsernamePasswordToken,即密码登录模式
- 通过implement AuthenticationToken的方式来实现自定义的登录方式和特殊的必需登录数据的索取
- 信息
授权的三要素
主体
资源
权限
角色
- 1、首先调用Subject.isPermitted*/hasRole*接口,其会委托给SecurityManager,而SecurityManager接着会委托给Authorizer;
- 2、Authorizer是真正的授权者,如果我们调用如isPermitted(“user:view”),其首先会通过PermissionResolver把字符串转换成相应的Permission实例;
- 3、在进行授权之前,其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限;
- 4、Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个Realm,会委托给ModularRealmAuthorizer进行循环判断,如果匹配如isPermitted*/hasRole*会返回true,否则返回false表示授权失败
ModularRealmAuthorizer进行多Realm匹配流程:
Realm授权
Shiro支持三种方式实现授权过程:
- 编码实现
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}
- 注解实现
@RequiresRoles("admin")
public void hello() {
//有权限
}
- JSP Taglig实现
<shiro:hasRole name="admin">
<!— 有权限 —>
</shiro:hasRole>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
- AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token):
- AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals):
@Autowired
private SessionDAO sessionDAO;
//获取会话数量
int size = sessionDAO.getActiveSessions().size()
//强制退出
Session session = sessionDAO.readSession(subject.getSession().getId());
sessionDAO.delete(session);
// logout,可作为强制退出
subject.logout();
Assert.isTrue(!subject.isAuthenticated());
springboot集成shiro
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.0</version>
</dependency>
@Component
public class OAuth2Realm extends AuthorizingRealm{
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
}
}
public class OAuth2Filter extends AuthenticatingFilter {
}
@Configuration
public class ShiroConfig {
@Bean("securityManager")
public SecurityManager securityManager(OAuth2Realm oAuth2Realm, SessionManager sessionManager){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(oAuth2Realm);
return securityManager;
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
}
}
- @RequiresPermissions
- @RequiresRoles
- 用户访问系统1的受保护资源,系统1发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
- sso认证中心发现用户未登录,将用户引导至登录页面
- 用户输入用户名密码提交登录申请
- sso认证中心校验用户信息,创建用户与sso认证中心之间的会话,称为全局会话,同时创建授权令牌
- sso认证中心带着令牌跳转会最初的请求地址(系统1)
- 系统1拿到令牌,去sso认证中心校验令牌是否有效
- sso认证中心校验令牌,返回有效,注册系统1
- 系统1使用该令牌创建与用户的会话,称为局部会话,返回受保护资源
- 用户访问系统2的受保护资源
- 系统2发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
- sso认证中心发现用户已登录,跳转回系统2的地址,并附上令牌
- 系统2拿到令牌,去sso认证中心校验令牌是否有效
- sso认证中心校验令牌,返回有效,注册系统2
- 系统2使用该令牌创建与用户的局部会话,返回受保护资源
- 局部会话存在,全局会话一定存在
- 全局会话存在,局部会话不一定存在
- 全局会话销毁,局部会话必须销毁
你可以通过博客园、百度、csdn、淘宝等网站的登录过程加深对单点登录的理解,注意观察登录过程中的跳转url与参数
- 用户向系统1发起注销请求
- 系统1根据用户与系统1建立的会话id拿到令牌,向sso认证中心发起注销请求
- sso认证中心校验令牌有效,销毁全局会话,同时取出所有用此令牌注册的系统地址
- sso认证中心向所有注册系统发起注销请求
- 各注册系统接收sso认证中心的注销请求,销毁局部会话
- sso认证中心引导用户至登录页面