一、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)方法从数据库中获取权限,然后和页面传过来的权限信息比对,判断是否有权限。对于授权流程,在后面的实例中会结合鉴权有更具体的描述。
以上是shiro认证和授权的一些原理、流程,对于shiro的基本概念这里讨论的并不是很详细,推荐这个系列博客,点击打开链接,我从这个博客系列学习到了很多,感谢作者。接下来我会结合具体的项目,整理出shiro的使用思路和实例。