一、shiro是什么
shiro是一个功能强大且容易使用的java安全框架,用它可以完成认证,授权,加密,会话管理,同时它也支持web集成,多线程,缓存等。
二、shiro的一些基础概念
Subject:主体,一般就是“用户”。
SecurityManager:安全管理,它是shiro的核心,管理所有的Subject以及认证,授权,会话管理,缓存等。
Authenticator:认证器,与身份认证有关。
Authorizer:授权器,与授权有关。
Realm:域,它是实现具体的身份认证,授权的地方。
sessionManager:会话管理器。
sessionDAO:session数据访问对象。
cacheManager:缓存控制器,与缓存有关,后面会讲到与redis集成。
三、认证(authentication)
首先ajax请求,将登陆账号和加密密码发送到后台服务器,后台封装包含账号和密码的UsernamePasswordToken对象
token.setRememberMe(rememberMe);//设置记住我 自动登录
SecurityUtils.getSubject().login(token);
具体的认证方法在我们自己实现的realm中,代码如下:
- /**
- * user login
- */
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException {
- UsernamePasswordToken authToken = (UsernamePasswordToken) token;
- String accountId = authToken.getUsername();
- String password = String.valueOf(authToken.getPassword());
- //登陆方法需要添加 平台参数 系统参数过滤
- AccountQuery query = new AccountQuery();
- query.setAccountId(accountId);
- query.setEmail(accountId);
- query.setMoblie(accountId);
- query.setPlatformId(platformLabel);
- query.setSubSystemId(systemLabel);
- //从数据库中获取账号
- com.isoftstone.securityframework.api.domain.Account domainAccount =
- (com.isoftstone.securityframework.api.domain.Account)accountManagerImpl.getAccount(accountId,platformLabel,systemLabel);
- //判断用户是否存在
- if (null == domainAccount){
- throw new UnknownAccountException(String.format("账号[%s]不存在!", accountId));
- }
- //检查用户密码是否匹配
- if (!domainAccount.getPassword().equals(password)){
- throw new IncorrectCredentialsException (String.format("[%s]密码错误!", accountId));
- }
- //检查账号是否激活
- if(STATUS_NOTACTIVATED == domainAccount.getStatus()){
- throw new AuthenticationException (String.format("用户名[%s]未激活!", accountId));
- }
- //检查账号是否已冻结
- if(STATUS_FREEZEEXCED == domainAccount.getStatus()){
- throw new AuthenticationException (String.format("账号[%s]已冻结", accountId));
- }
- //检查账号身份是否冻结
- AccountCommon accountCommon = domainAccount.getAccCommon();
- if(accountCommon!=null){
- if(accountCommon.getIsBuyer()!=4 &&accountCommon.getIsSaler()!=4){
- throw new AuthenticationException (String.format("账号[%s]已经被冻结", accountId));
- }
- }
- //设置登录时间
- domainAccount.setLastLoginTime(DateUtils.getToday(DateUtils.TIMEF_FORMAT));
- accountManagerImpl.modify(domainAccount);
- Account authAccount = new Account();
- this.copyPropertiesToAuthAccount(domainAccount,authAccount);
- //设置已认证的用户信息到用户对象中
- SimpleAuthenticationInfo simpleAuthInfo = new SimpleAuthenticationInfo(authAccount,authAccount.getPassword(),getName());
- return simpleAuthInfo;
- }
四、授权(authorization)
由图可见,当调用Subject.isPermitted()或Subject.hasRole()时,shiro会交给Security Manager,然后Security Manager又会将授权交个Authorizer授权者,而Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个Realm,会委托给ModularRealmAuthorizer 进行循环判断,如果匹配如isPermitted*/hasRole*会返回true,否则返回false表示授权失败。
1.1、如果调用hasRole*,则直接获取AuthorizationInfo.getRoles()与传入的角色比较即可;
1.2、首先如果调用如isPermitted(“user:view”),首先通过PermissionResolver 将权限字符串
转换成相应的Permission 实例,默认使用WildcardPermissionResolver,即转换为通配符的WildcardPermission;
具体的授权方法在我们自己实现的realm中,代码如下:
- /**
- * user permission query(Get authorization info from Cache first or get info from remote service )
- */
- @SuppressWarnings("unchecked")
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
- Account account1 = (Account)getAvailablePrincipal(principals);
- com.isoftstone.securityframework.api.domain.Account account = (com.isoftstone.securityframework.api.domain.Account) accountManagerImpl.get(account1.getId());
- //加载权限
- List<com.isoftstone.securityframework.api.Permission> perms = new ArrayList<Permission>();
- //从数据库中查询权限
- perms = rolePermissionRealm.getSubjectPermission(account);
- Set<String> permSet = new HashSet<String>();
- if(null != perms){
- for (Permission perm : perms) {
- String permissionName = perm.getPermissionName();
- //将前缀去掉
- int beginIndex = platformLabel.length() +systemLabel.length()+2;
- permissionName = permissionName.substring(beginIndex);
- permSet.add(permissionName);
- }
- }
- SimpleAuthorizationInfo simpleAuthInfo = new SimpleAuthorizationInfo();
- simpleAuthInfo.setStringPermissions(permSet);
- return simpleAuthInfo;
- }
当我们调用Subject.isPermitted()进行鉴权的时候,shiro会先去缓存中找当前登录账号的授权信息SimpleAuthorizationInfo,如果没有找到,它会调用上述realm中的doGetAuthorizationInfo(PrincipalCollection principals)方法从数据库中获取权限,然后和页面传过来的权限信息比对,判断是否有权限。对于授权流程,在后面的实例中会结合鉴权有更具体的描述。