Shiro 之Subject、SecurityManager、Realm源码分析
一、简介
Shiro 提供了一些常见的Realm 实现如JdbcRealm,从数据库获取相关用户名、密码等信息作为认证、授权数据来源,但是非常的不方便,JdbcRealm 对数据表名、字段名都有硬性规定,非常不灵活。
二、配置INI 文件
[main]
#自定义Reaml 实现认证、授权
realm=com.vincent.UserRealm
#securityManager 对象是配置文件提供的SecurityManager 默认对象
securityManager.realm=$realm
三、自定义Realm
Reaml 接口实现类有很多,继承体系结构如下:
CachingRealm 实现缓存的支持
AuthenticationRealm 实现对认证支持
AuthorizingRealm 实现对认证、授权的支持。通常都继承该Realm 实现Realm 自定义。
新建UserRealm.java
package com.vincent;
import java.util.List;
import java.util.Arrays;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class UserRealm extends AuthorizingRealm{
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
if(((String)principals.getPrimaryPrincipal()).equalsIgnoreCase("vincent")) {
//创建授权对象
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//添加角色信息
List<String> roles = Arrays.asList("role_admin","role_test");
info.addRoles(roles);
//添加权限信息
List<String> permissions = Arrays.asList("perm:add","perm:delete");
info.addStringPermissions(permissions);
return info;
}
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
if(((String)token.getPrincipal()).equalsIgnoreCase("vincent")) {
//创建登录认证对象
AuthenticationInfo info = new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), super.getName());
return info;
}
else {
return null;
}
}
}
四、App.java 测试
package com.vincent;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityUtils.setSecurityManager(factory.getInstance());
Subject subject = SecurityUtils.getSubject();
System.out.println(subject);
AuthenticationToken token = new UsernamePasswordToken("vincent","123");
try {
subject.login(token);
System.out.println(subject.isAuthenticated());
System.out.println(subject.hasRole("role_admin"));
System.out.println(subject.hasRole("role_test"));
System.out.println(subject.isPermitted("perm:add"));
System.out.println(subject.isPermitted("perm:delete"));
}
catch (AuthenticationException e) {
e.printStackTrace();
}
Subject subject2 = SecurityUtils.getSubject();
AuthenticationToken token2 = new UsernamePasswordToken("vincent2","123");
try {
subject.login(token2);
System.out.println(subject2.isAuthenticated());
System.out.println(subject2.hasRole("role_admin"));
System.out.println(subject2.hasRole("role_test"));
System.out.println(subject2.isPermitted("perm:add"));
System.out.println(subject2.isPermitted("perm:delete"));
}
catch (AuthenticationException e) {
e.printStackTrace();
}
}
}
运行效果如下:
源码分析说明:
Subject 为用户操作登录、授权、权限检查的主体接口,实现类为DelegatingSubject,登录功能被委托给SecurityManager 实现类实现:
public void login(AuthenticationToken token) throws AuthenticationException {
clearRunAsIdentitiesInternal();
Subject subject = securityManager.login(this, token);
PrincipalCollection principals;
String host = null;
if (subject instanceof DelegatingSubject) {
DelegatingSubject delegating = (DelegatingSubject) subject;
//we have to do this in case there are assumed identities - we don't want to lose the 'real' principals:
principals = delegating.principals;
host = delegating.host;
} else {
principals = subject.getPrincipals();
}
if (principals == null || principals.isEmpty()) {
String msg = "Principals returned from securityManager.login( token ) returned a null or " +
"empty value. This value must be non null and populated with one or more elements.";
throw new IllegalStateException(msg);
}
this.principals = principals;
this.authenticated = true;
if (token instanceof HostAuthenticationToken) {
host = ((HostAuthenticationToken) token).getHost();
}
if (host != null) {
this.host = host;
}
Session session = subject.getSession(false);
if (session != null) {
this.session = decorate(session);
} else {
this.session = null;
}
}
SecurityManager 实现类为DefaultSecurityManager:
SecurityManager 继承体系:
SecurityManager 结构:
SecurityManager 实现认证、授权接口
AuthenticatingSecurityManager 中有默认的认证器实现:
ModularRealmAuthenticator 认证方法:
从以上大体可分析出Sbject、Realm、SecurityManager 关系:
Subject 负责用户安全操作,SecurityManager 协调、管理Subject、Realm,Realm 提供相关认证、授权的数据来源。
下一篇: python自动化测试实例解析