Shiro 之Subject、SecurityManager、Realm源码分析

一、简介

Shiro 提供了一些常见的Realm 实现如JdbcRealm,从数据库获取相关用户名、密码等信息作为认证、授权数据来源,但是非常的不方便,JdbcRealm 对数据表名、字段名都有硬性规定,非常不灵活。

二、配置INI 文件

[main]

#自定义Reaml 实现认证、授权
realm=com.vincent.UserRealm

#securityManager 对象是配置文件提供的SecurityManager 默认对象
securityManager.realm=$realm

三、自定义Realm

Reaml 接口实现类有很多,继承体系结构如下:
Shiro 之Subject、SecurityManager、Realm源码分析_第1张图片
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源码分析_第2张图片

源码分析说明:
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源码分析_第3张图片

SecurityManager 继承体系:
Shiro 之Subject、SecurityManager、Realm源码分析_第4张图片

SecurityManager 结构:
在这里插入图片描述

SecurityManager 实现认证、授权接口

AuthenticatingSecurityManager 中有默认的认证器实现:
Shiro 之Subject、SecurityManager、Realm源码分析_第5张图片

ModularRealmAuthenticator 认证方法:
Shiro 之Subject、SecurityManager、Realm源码分析_第6张图片

Shiro 之Subject、SecurityManager、Realm源码分析_第7张图片

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

你可能感兴趣的:(Shiro)