Shiro 之Subject、SecurityManager、Realm源码分析

一、简介 Shiro 提供了一些常见的Realm 实现如JdbcRealm,从数据库获取相关用户名、密码等信息作为认证、授权数据来源,但是非常的不方便,JdbcRealm 对数据表名、字段名都有硬性规定,非常不灵活。
二、配置INI 文件 [main]
#自定义Reaml 实现认证、授权
realm=com.vincent.UserRealm
#securityManager 对象是配置文件提供的SecurityManager 默认对象
securityManager.realm=$realm
三、自定义Realm 【Shiro 之Subject、SecurityManager、Realm源码分析】Reaml 接口实现类有很多,继承体系结构如下:
Shiro 之Subject、SecurityManager、Realm源码分析
文章图片

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 roles = Arrays.asList("role_admin","role_test"); info.addRoles(roles); //添加权限信息 List 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 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(); }} }

运行效果如下:
Shiro 之Subject、SecurityManager、Realm源码分析
文章图片

源码分析说明:
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:
Shiro 之Subject、SecurityManager、Realm源码分析
文章图片

SecurityManager 继承体系:
Shiro 之Subject、SecurityManager、Realm源码分析
文章图片

SecurityManager 结构:
Shiro 之Subject、SecurityManager、Realm源码分析
文章图片

SecurityManager 实现认证、授权接口
AuthenticatingSecurityManager 中有默认的认证器实现:
Shiro 之Subject、SecurityManager、Realm源码分析
文章图片

ModularRealmAuthenticator 认证方法:
Shiro 之Subject、SecurityManager、Realm源码分析
文章图片

Shiro 之Subject、SecurityManager、Realm源码分析
文章图片

从以上大体可分析出Sbject、Realm、SecurityManager 关系:
Subject 负责用户安全操作,SecurityManager 协调、管理Subject、Realm,Realm 提供相关认证、授权的数据来源。

    推荐阅读