美文网首页
(二)三大组件

(二)三大组件

作者: guideEmotion | 来源:发表于2019-06-22 20:25 被阅读0次

一 Realm

Realm:域,Realm 充当了 Shiro 与应用安全数据间的“桥梁”或者“连接器”

也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro 会从应用配置的 Realm 中查找用户及其权限信息。从这个意义上讲,Realm 实质上是一个安全相关的 DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给 Shiro 。当配置 Shiro时,你必须至少指定一个 Realm ,用于认证和(或)授权。配置多个 Realm 是可以的,但是至少需要一个。
Shiro 内置了可以连接大量安全数据源(又名目录)的 Realm,如 LDAP、关系数据库(JDBC)、类似 INI 的文本配置资源以及属性文件等。如果缺省的 Realm 不能满足需求,你还可以插入代表自定义数据源的自己的 Realm 实现。

功能

Realm能做的工作主要有以下几个方面:

  • 身份验证(getAuthenticationInfo 方法)验证账户和密码,并返回相关信息
  • 权限获取(getAuthorizationInfo 方法) 获取指定身份的权限,并返回相关信息
  • 令牌支持(supports方法)判断该令牌(Token)是否被支持
    令牌有很多种类型,例如:HostAuthenticationToken(主机验证令牌),UsernamePasswordToken(账户密码验证令牌)

身份认证

根据传进来的 Token,返回用户的验证信息

Token

比如最常见的UsernamePasswordToken

public class UsernamePasswordToken implements HostAuthenticationToken, RememberMeAuthenticationToken {
    private String username;
    private char[] password;
    private boolean rememberMe;
    private String host;
    ...

用户验证信息

就是用户验证通过后,返回给系统的信息。例如:用户登录验证的话,一般来说,返回给系统的“用户验证信息”就应该是这个用户的“用户名和密码”。但也可以返回其它信息,例如返回用户的“邮箱地址和登录密码”信息,做为“用户验证信息”(失败直接抛出AuthenticationException异常了)

认证过程

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

    AuthenticationInfo info = getCachedAuthenticationInfo(token);
    // doGetAuthenticationInfo方法的内容,由各个子类来实现。
    // 主要是用来取得我们保存的“用户验证信息”,例如DB里保存的密码(具体看JdbcRealm的方法实现)
    if (info == null) {
        info = doGetAuthenticationInfo(token);
        ...
    }
    // 在这里,把用户提交的信息(Token)和我们保存的“用户验证信息”进行比较
    // 如果不通过,直接抛出定义好的异常。
    if (info != null) {
        assertCredentialsMatch(token, info);
    } else {

    return info;
}

权限认证

public abstract class AuthorizingRealm extends AuthenticatingRealm implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware {
    ...
    protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
        if (principals == null) {
            return null;
        } else {
            AuthorizationInfo info = null;
            if (log.isTraceEnabled()) {
                log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");
            }

            Cache<Object, AuthorizationInfo> cache = this.getAvailableAuthorizationCache();
            Object key;
            if (cache != null) {
                if (log.isTraceEnabled()) {
                    log.trace("Attempting to retrieve the AuthorizationInfo from cache.");
                }

                key = this.getAuthorizationCacheKey(principals);//先从principals获取key
                info = (AuthorizationInfo)cache.get(key);//根据key从缓存中获取AuthorizationInfo
                if (log.isTraceEnabled()) {
                    if (info == null) {
                        log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]");
                    } else {
                        log.trace("AuthorizationInfo found in cache for principals [" + principals + "]");
                    }
                }
            }

            if (info == null) {//cache中没有对应用户的缓存数据
                info = this.doGetAuthorizationInfo(principals);//查询权限
                if (info != null && cache != null) {
                    if (log.isTraceEnabled()) {
                        log.trace("Caching authorization info for principals: [" + principals + "].");
                    }

                    key = this.getAuthorizationCacheKey(principals);
                    cache.put(key, info);
                }
            }

            return info;
        }
    }

getAuthorizationInfo的执行时机

  1. subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己去调用这个是否有什么角色或者是否有什么权限的时候
  2. @RequiresRoles(“admin”) :在方法上加注解的时候;
  3. 标签:[@shiro.hasPermission name = “admin”][/@shiro.hasPermission]:在页面上加shiro标签的时候,即进这个页面的时候扫描到有这个标签的时候。

实现

image.png

**一个只有登陆认证的Realm **

public class MyRealm1 implements Realm {  
    @Override  
    public String getName() {  
        return "myrealm1";  
    }  
    @Override  
    public boolean supports(AuthenticationToken token) {  
        //仅支持UsernamePasswordToken类型的Token  
        return token instanceof UsernamePasswordToken;   
    }  
    @Override  
    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {  
        String username = (String)token.getPrincipal();  //得到用户名  
        String password = new String((char[])token.getCredentials()); //得到密码  
        if(!"zhang".equals(username)) {  
            throw new UnknownAccountException(); //如果用户名错误  
        }  
        if(!"123".equals(password)) {  
            throw new IncorrectCredentialsException(); //如果密码错误  
        }  
        //如果身份认证验证成功,返回一个AuthenticationInfo实现;  
        return new SimpleAuthenticationInfo(username, password, getName());  
    }  
}   

较完整

@Component
public class LoginRealm extends AuthorizingRealm{

    @SuppressWarnings("SpringJavaAutowiringInspection")//忽略警告,下同
    @Resource(name = "roleServiceImpl")
    private RoleService roleService;

    @SuppressWarnings("SpringJavaAutowiringInspection")//忽略警告,下同
    @Resource(name = "viewEmployeeMiPsdServiceImpl")
    private ViewEmployeeMiPsdService viewEmployeeMiPsdService;



    /**
     *      获取身份信息,我们可以在这个方法中,从数据库获取该用户的权限和角色信息
     *      当调用权限验证时,就会调用此方法
     */
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        String code = (String) getAvailablePrincipal(principalCollection);

        Role role = null;
        ViewEmployeeMiPsd viewEmployeeMiPsd = null;
        viewEmployeeMiPsd = viewEmployeeMiPsdService.findByCode(code);
        //通过用户名从数据库获取角色权限集
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Set<String> r = new HashSet<>();
        if (role != null) {
            String[] roles = role.getRolename().split("\\+");
            for(int i = 0;i < roles.length; i++){
                r.add(roles[i].toString());
            }
            //放入该用户权限信息
            info.setRoles(r);
        }

        return info;
    }

    /**
     * 在这个方法中,进行身份验证
     * login时调用
     */
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //工号
        String code = (String) token.getPrincipal();
        //密码
        String password = new String((char[])token.getCredentials());

        ViewEmployeeMiPsd viewEmployeeMiPsd = null;
        viewEmployeeMiPsd = viewEmployeeMiPsdService.findByCode(code);

        if (viewEmployeeMiPsd == null) {
            //没有该用户
            throw new UnknownAccountException();
        } else if (!password.equals(viewEmployeeMiPsd.getPsd())) {
            //密码错误
            throw new IncorrectCredentialsException();
        }

        //身份验证通过,返回一个身份信息
        AuthenticationInfo aInfo = new SimpleAuthenticationInfo(code,password,getName());

        return aInfo;
    }
}

二 Subject

Subject是一个抽象的概念,通常我们理解为用户,但它可以是任何与系统交互的“东西”
Subject一词是一个安全术语,其基本意思是“当前的操作用户
称之为“用户”并不准确,因为“用户”一词通常跟人相关。在安全领域,术语“Subject”可以是人,也可以是第三方进程、后台帐户(Daemon Account)、定时作业(Corn Job)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。
在程序中你都能轻易的获得Subject,允许在任何需要的地方进行安全操作。每个Subject对象都必须与一个SecurityManager进行绑定,你访问Subject对象其实都是在与SecurityManager里的特定Subject进行交互。

获取方式

        Subject subject = SecurityUtils.getSubject()

用途

操作session

Session session = subject.getSession();
session.setAttribute( "someKey", "aValue" ); 

这里的Session并不是HttpSession,而是shiro为我们提供的,它的操作与HttpSession一样,他们最大的区别就是shiro session不需要依赖http服务器,下图是shiro Session的实现类。


image.png
  1. 默认情况下shiro Session的实现的是DelegatingSession.
  2. 而当我们整合HTTP服务器时,shiro Session会自动实现HttpServletSession,再来看一下HttpServletSession的实现

三 SecurityManager

安全管理器,是Shiro的核心

接口继承

public interface SecurityManager extends Authenticator, Authorizer, SessionManager {
    //登录方法
    Subject login(Subject subject, AuthenticationToken authenticationToken) throws AuthenticationException;
    //注销方法
    void logout(Subject subject);
    //创建subject
     Subject createSubject(SubjectContext context);
 
}

继承的三个接口分别是

  1. Authenticator(认证器) ,用户登录时候会调用
public interface Authenticator {  

    //初始的认证方法,入参是一个令牌对象,返回值是一个包装好的身份信息
 public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)  throws AuthenticationException;
}
  1. Authorizer(授权器) 检查是否具有相关的权限.这个接口定义的都是检查权限和角色的方法
  2. SessionManager(会话管理器)

继承体系

image.png

与spring整合的默认安全管理器就是图中标注的那个,一般如果不自己定义的话,就会使用这个东西
很有意思的集成体系,每次继承都实现一些接口,或者实现一些特色的功能,

  1. CacheSecurityManager 和缓存相关
public abstract class CachingSecurityManager implements SecurityManager, Destroyable, CacheManagerAware, EventBusAware 

  1. RealmSecurityManager 这个抽象类保存了所有的Realm实现
public abstract class RealmSecurityManager extends CachingSecurityManager {
    private Collection<Realm> realms;

  1. AuthenticatingSecurityManager 这个方法实现了认证的部分功能

  1. AuthorizingSecurityManager 完成授权部分功能

  1. SessionsSecurityManager 注入了会话管理器

  1. DefaultSecurityManager 默认非web环境的安全管理器

  1. DefaultWebSecurityManager 默认的继承web层次的安全管理器

参考

  1. http://how2j.cn/k/shiro/shiro-database/1721.html#nowhere
  2. Shiro 中的 Realm
  3. Shiro的Subject对象详解
  4. https://blog.csdn.net/qq_35448976/article/details/79102828
  5. Shiro源码学习---SecurityManager

相关文章

网友评论

      本文标题:(二)三大组件

      本文链接:https://www.haomeiwen.com/subject/tzqmqctx.html