Java-Shiro(四):Shiro Realm讲解(一)Realm介绍

Realm简介

Realm:英文意思‘域’,在Shiro框架中,简单理解它存储(读取配置、读取数据库、读取内存中)用户信息,而且它充当了Shiro与应用安全数据间的“桥梁”(“链接器”),当对某个用户执行认证(登陆)和受权(用户用有的角色、资源,或者说访问控制)验证时,Shiro会从应用配置的Realm中查找用户以及权限信息。html

从它的在Shiro矿建中所起的做用来说,Realm至关因而一个安全相关的DAO:它封装了数据源的链接细节,并在须要时将相关数据提供给Shiro。当配置Shiro时,你必须指定一个Realm(固然容许配置多个,指定多Realm验证策略,多个Realm来工做),用于认证、受权。java

按照Realm中用户和权限信息存储方式不一样,在Shiro框架内部定义了如下Realm:spring

1)IniRealm:将用户、角色信息配置到*.ini文件中,使用IniRealm加载数据信息,提供认证、受权功能;数据库

2)PropertiesRealm:将用户、角色信息配置到*.properties文件中,使用PropertiesRealm加载数据信息,提供认证、受权功能;安全

3)jdbcRealm:将用户、角色信息配置到关系型数据库总,外部能够指定DataSource信息,默认内部包含了认证、受权SQL,默认数据库表:users、user_roles、roles_permissions;mvc

4)*Ldap*/ActiveDirectory*:LDAP目录服务是由目录数据库和一套访问协议组成的系统,其实就是把用户、角色信息存储到这样的一个Ad系统,经过Ldap/Jndi/等方式链接读取数据,至于shiro功能与上边3种一致。app

5)自定义Realm:上图中MyRealm就是我自定义的一个Realm,若是缺省的Realm不能知足需求时,咱们还能够自定义数据源本身的Realm实现。框架

Realm功能

Realm主要负责的功能包含如下:

经过上边Realm类结构图,能够看出IniRealm、PropertiesRealm、JdbcRealm等的最上层父类是 AuthorizingRealm,而它又继承了AuthenticatingRealm,咱们查看类中定义:分布式

AuthenticatingRealm.java中惟一抽象方法:(该方法提供身份认证使用,具体能够参考缺省Realm的实现)ide

protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

AuthorizingRealm.java中惟一抽象方法:(该方法给认证经过的用户赋值权限、资源使用,具体能够参考缺省Realm的实现)

protected abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals);

1)身份认证:调用Realm#getAuthenticationinfo()方法,验证用户输入的帐户和密码(内部调用Realm#doGetAuthenticationInfo()方法进行用户认证),并返回与Realm数据对比验证结果相关信息;查看AuthenticationRealm#getAuthenticationInfo()源码:

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { AuthenticationInfo info = getCachedAuthenticationInfo(token); if (info == null) { //otherwise not cached, perform the lookup:
            info = doGetAuthenticationInfo(token); log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info); if (token != null && info != null) { cacheAuthenticationInfoIfPossible(token, info); } } else { log.debug("Using cached authentication info [{}] to perform credentials matching.", info); } if (info != null) { assertCredentialsMatch(token, info); } else { log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token); } return info; }

2)权限、资源获取:调用Realm#getAuthorizationinfo()方法,获取指定身份的权限(内部调用Realm#doGetAuthorizationInfo()方法进行制定身份的权限),并返回相关信息;查看AuthorizingRealm#getAuthorizationInfo()源码:

protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) { if (principals == null) { return null; } AuthorizationInfo info = null; if (log.isTraceEnabled()) { log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]"); } Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache(); if (cache != null) { if (log.isTraceEnabled()) { log.trace("Attempting to retrieve the AuthorizationInfo from cache."); } Object key = getAuthorizationCacheKey(principals); info = cache.get(key); 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) { // Call template method if the info was not found in a cache
            info = doGetAuthorizationInfo(principals); // If the info is not null and the cache has been created, then cache the authorization info.
            if (info != null && cache != null) { if (log.isTraceEnabled()) { log.trace("Caching authorization info for principals: [" + principals + "]."); } Object key = getAuthorizationCacheKey(principals); cache.put(key, info); } } return info; }

3)断定Token是否支持:调用Realm#supports()方法,验证是否支持令牌(Token)。

备注:Token在Shiro中包含几种类型:HostAuthenticationToken(主机验证令牌),UsernamePasswordToken(帐户密码验证令牌),RememberMeAuthenticationToken(记录上次登陆用户Token)。

何时调用Realm#getAuthenticationinfo()、何时调用 Realm#getAuthorizationinfo()?

Realm iniRealm = new IniRealm("classpath:shiroAuthorizer.ini"); DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(iniRealm); SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123"); // 调用 Realm#getAuthenticationinfo()
 subject.login(token); System.out.println("是否定证经过:" + subject.isAuthenticated()); // 调用 Realm#getAuthorizationinfo()
        System.out.println("是否受权admin角色:" + subject.hasRole("admin")); System.out.println("是否拥有user:update资源:" + subject.isPermitted("user:update")); System.out.println("是否拥有user:delete资源:" + subject.isPermitted("user:delete")); System.out.println("是否拥有user:create资源:" + subject.isPermitted("user:create")); subject.logout(); System.out.println("是否定证经过:" + subject.isAuthenticated()); System.out.println("是否受权admin角色:" + subject.hasRole("admin")); System.out.println("是否拥有user:update资源:" + subject.isPermitted("user:update")); System.out.println("是否拥有user:delete资源:" + subject.isPermitted("user:delete")); System.out.println("是否拥有user:create资源:" + subject.isPermitted("user:create"));

1)何时调用身份认证(Realm#getAuthenticationinfo())

从Subject#login()代码进行跟踪,寻找Subject#login()底层调用的代码以下:

DelegatingSubject#login()方法调用-》DefaultSecurityManager#login(),内部调用-》AbstractAuthenticator#authenticate()方法,内部调用-》ModularRealmAuthenticator#doAuthenticate()方法,该方法源码以下:

/** * Subject#login最终底层调用的方法就是该方法 */
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { assertRealmsConfigured(); Collection<Realm> realms = getRealms(); if (realms.size() == 1) { return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken); } else { return doMultiRealmAuthentication(realms, authenticationToken); } } /** * 单个Realm配置时,调用该法 */
    protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) { if (!realm.supports(token)) { String msg = "Realm [" + realm + "] does not support authentication token [" + token + "].  Please ensure that the appropriate Realm implementation is " +
                    "configured correctly or that the realm accepts AuthenticationTokens of this type."; throw new UnsupportedTokenException(msg); } AuthenticationInfo info = realm.getAuthenticationInfo(token); if (info == null) { String msg = "Realm [" + realm + "] was unable to find account data for the " +
                    "submitted AuthenticationToken [" + token + "]."; throw new UnknownAccountException(msg); } return info; } /** * 多个Realm配置时,调用该方法 */
    protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) { AuthenticationStrategy strategy = getAuthenticationStrategy(); AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token); if (log.isTraceEnabled()) { log.trace("Iterating through {} realms for PAM authentication", realms.size()); } for (Realm realm : realms) { aggregate = strategy.beforeAttempt(realm, token, aggregate); if (realm.supports(token)) { log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm); AuthenticationInfo info = null; Throwable t = null; try { info = realm.getAuthenticationInfo(token); } catch (Throwable throwable) { t = throwable; if (log.isDebugEnabled()) { String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:"; log.debug(msg, t); } } aggregate = strategy.afterAttempt(realm, token, info, aggregate, t); } else { log.debug("Realm [{}] does not support token {}.  Skipping realm.", realm, token); } } aggregate = strategy.afterAllAttempts(token, aggregate); return aggregate; }

备注:

1)因为Subject是一个接口类型,所以上边代码找的是 DelegatingSubject.java类中的代码;

2)Subject的类结构图:

2)何时调用受权( Realm#getAuthorizationinfo())

从调用Subject#isPermitted()源码能够看出内部调用了Subject#getAuthorizationInfo()方法:

public boolean isPermitted(PrincipalCollection principals, Permission permission) { AuthorizationInfo info = getAuthorizationInfo(principals); return isPermitted(permission, info); }

从调用Subject#hasRole()源码能够看出内部调用了Subject#getAuthorizationInfo()方法:

public boolean hasRole(PrincipalCollection principal, String roleIdentifier) { AuthorizationInfo info = getAuthorizationInfo(principal); return hasRole(roleIdentifier, info); }

不过,getAuthorizationInfo 的执行调用方式包括上面的总共有三个:

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

参考资料:

Shiro 中的 Realm

SSM整合shiro实现多用户表多Realm统一登陆认证(大章附代码)

不错的视屏教程,很实用:

https://www.bilibili.com/video/av22573274/?p=13

https://www.bilibili.com/video/av74805893?p=18 (针对java proj/springmvc整合/分布式用法都有系列讲解,值得推荐)

文章实战:https://blog.csdn.net/bieleyang/article/category/7140250

相关文章
相关标签/搜索