Shiro several key categories

Shiro Spring integration that needs to be configured SecurityManager, Realm, ShiroFilterFactoryBean three classes. In a Web environment SecurityManager general configuration DefaultWebSecurityManager, if you need to expand or customize some additional features, you can configure the inherited class DefaultWebSecurityManager of; Realm need to inherit AuthorizingRealm abstract class reconfiguration, if there are multiple Realm, then you need to configure inheritance achieve ModularRealmAuthenticator of class; ShiroFilterFactoryBean mainly offer ShiroFilter, you can configure some intercepted resources. Next, some of the core classes to sum up.

SecurityManager

This class inherits three interfaces, also provided additional login, exit, and the user is created.

 /**
  * 所有与安全有关的操作都会与SecurityManager交互
  * 扩展了authenticator、authorizer和sessionmanager接口
  */
public interface SecurityManager extends Authenticator, Authorizer, SessionManager {

    Subject login(Subject subject, AuthenticationToken authenticationToken) throws AuthenticationException;
    
    void logout(Subject subject);
  
    Subject createSubject(SubjectContext context);
}

/**
 * 认证验证,登录校验
 *
 */
public interface Authenticator {

    /**
     * AuthenticationToken 登录未验证的数据
     * AuthenticationInfo 身份验证/登录过程相关的帐户信息。
     *
     */
    public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)
            throws AuthenticationException;
}

/**
 * 用户授权,权限校验
 *
 */
public interface Authorizer {

    boolean[] isPermitted(PrincipalCollection subjectPrincipal, String... permissions);

    boolean[] isPermitted(PrincipalCollection subjectPrincipal, List<Permission> permissions);

    boolean isPermittedAll(PrincipalCollection subjectPrincipal, String... permissions);

    boolean isPermittedAll(PrincipalCollection subjectPrincipal, Collection<Permission> permissions);

    void checkPermissions(PrincipalCollection subjectPrincipal, String... permissions) throws AuthorizationException;

    void checkPermissions(PrincipalCollection subjectPrincipal, Collection<Permission> permissions) throws AuthorizationException;

    boolean hasRole(PrincipalCollection subjectPrincipal, String roleIdentifier);

    boolean[] hasRoles(PrincipalCollection subjectPrincipal, List<String> roleIdentifiers);

    boolean hasAllRoles(PrincipalCollection subjectPrincipal, Collection<String> roleIdentifiers);

    void checkRole(PrincipalCollection subjectPrincipal, String roleIdentifier) throws AuthorizationException;

    void checkRoles(PrincipalCollection subjectPrincipal, Collection<String> roleIdentifiers) throws AuthorizationException;

    void checkRoles(PrincipalCollection subjectPrincipal, String... roleIdentifiers) throws AuthorizationException;
    
}

/**
 * 会话管理
 */
public interface SessionManager {

    /**
     * 基于指定的上下文初始化数据启动一个新Session,Session通常交由SessionFactory创建
     * 
     */
    Session start(SessionContext context);

    /**
     * 通过SessionKey查找Session
     *
     */
    Session getSession(SessionKey key) throws SessionException;
}

SecurityManager Web portion of the source code implementation is shown below. From the default constructor can be seen when creating this implementation of SecurityManager, it sets a series of default values, such as ServletContainerSessionManager, CookieRememberMeManager and so on. The isHttpSessionMode method to determine whether the HttpSession, or realize their own Session.

public class DefaultWebSecurityManager extends DefaultSecurityManager implements WebSecurityManager {

    public DefaultWebSecurityManager() {
        super();
        DefaultWebSessionStorageEvaluator webEvalutator = new DefaultWebSessionStorageEvaluator();  
        ((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(webEvalutator);
        this.sessionMode = HTTP_SESSION_MODE;
        setSubjectFactory(new DefaultWebSubjectFactory());
        setRememberMeManager(new CookieRememberMeManager());
        setSessionManager(new ServletContainerSessionManager());
        webEvalutator.setSessionManager(getSessionManager());
    }
    
    public boolean isHttpSessionMode() {
        SessionManager sessionManager = getSessionManager();
        return sessionManager instanceof WebSessionManager &&                                           ((WebSessionManager)sessionManager).isServletContainerSessions();       
    }
    ...
}

The following is a SecurityManager achieve SessionManager implementation class interface, which you can see and there is no practical method SessionManager SecurityManager interface process, instead of using the combined mode, the actual SessionManager SecurityManager as member variables, the actual processing is referred sessionManager to deal with. And after the completion of initialization SessionManager default DefaultSessionManager (in DefaultWebSecurityManager a new class of inherited, in order to ServletContainerSessionManager) If, after SessionManager achieve CacheManagerAware interfaces, it will be set to CacheManager also together in SessionManager.

public abstract class SessionsSecurityManager extends AuthorizingSecurityManager {
    private SessionManager sessionManager;

    public SessionsSecurityManager() {
        super();
        this.sessionManager = new DefaultSessionManager();
        applyCacheManagerToSessionManager();
    }

     protected void applyCacheManagerToSessionManager() {
        if (this.sessionManager instanceof CacheManagerAware) {
            ((CacheManagerAware) this.sessionManager).setCacheManager(getCacheManager());
        }
    }

    public void setSessionManager(SessionManager sessionManager) {
        this.sessionManager = sessionManager;
        afterSessionManagerSet();
    }

    protected void afterSessionManagerSet() {
        applyCacheManagerToSessionManager();
        applyEventBusToSessionManager();
    }

    public SessionManager getSessionManager() {
        return this.sessionManager;
    }

    public Session start(SessionContext context) throws AuthorizationException {
        return this.sessionManager.start(context);
    }

    public Session getSession(SessionKey key) throws SessionException {
        return this.sessionManager.getSession(key);
    }
}

SecurityManager system inherited from the point of view, each successor will add a member variable, and the method is also open to the public are handled by the member. So for now, SecurityManager is through inheritance system and the combination of models, to enrich its actual function, and Shiro individual components are linked to together. SecurityManager is thread safe and applications really need only one can, therefore Shiro provides SecurityUtils let us bind it as global, to facilitate subsequent operations.

Realm

Realm: domain, Shiro acquired from safety data (such as users, roles, permissions) from the Realm, that is SecurityManager to authenticate users, it needs to obtain the appropriate user from the Realm are compared to determine the identity of the user is legitimate; need from the Realm to give the user the role / permissions to verify that the user can operate; can Realm as dataSource, i.e. secure data source. AuthorizingRealm typically implemented by a program category, if there are multiple implementations, the method further ModularRealmAuthenticator doAuthenticate of the need to be rewritten to specify the corresponding processing AuthenticationToken Realm. In addition AuthorizingRealm provide set caching, encryption and permissions related functions.

public interface Realm {

    /**
     * 返回应用中Realm的唯一名字
     */
    String getName();

    /**
     * 多Realm中,该Realm是否匹配AuthenticationToken
     */
    boolean supports(AuthenticationToken token);

    /**
     * 依据未认证的AuthenticationToken,返回认证后的AuthenticationInfo
     */
    AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

}

ModularRealmAuthenticator

Authenticator function is to verify that the user account is Shiro API authentication in the core entry point. If authentication is successful, it will return AuthenticationInfo authentication information; this information includes the identity and credentials; if the validation fails will throw an exception corresponding AuthenticationException achieved. The default implementation class is ModularRealmAuthenticator, you can see the entire process of verification from this class, but also provides a process AuthenticationListener to listen authentication (login is successful main event, logon failure events and exits).

public class ModularRealmAuthenticator extends AbstractAuthenticator {

    private static final Logger log = LoggerFactory.getLogger(ModularRealmAuthenticator.class);

    private Collection<Realm> realms;

    private AuthenticationStrategy authenticationStrategy;

    public ModularRealmAuthenticator() {
        this.authenticationStrategy = new AtLeastOneSuccessfulStrategy();
    }

    public void setRealms(Collection<Realm> realms) {
        this.realms = realms;
    }

    protected Collection<Realm> getRealms() {
        return this.realms;
    }

    public AuthenticationStrategy getAuthenticationStrategy() {
        return authenticationStrategy;
    }

    public void setAuthenticationStrategy(AuthenticationStrategy authenticationStrategy) {
        this.authenticationStrategy = authenticationStrategy;
    }

    protected void assertRealmsConfigured() throws IllegalStateException {
        Collection<Realm> realms = getRealms();
        if (CollectionUtils.isEmpty(realms)) {
            String msg = "Configuration error:  No realms have been configured!  One or more realms must be " +
                    "present to execute an authentication attempt.";
            throw new IllegalStateException(msg);
        }
    }

    /**
     * 单Realm的校验,最后调用realm.getAuthenticationInfo方法来通过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;
    }

    /**
     * 多Realm的校验,还需要考虑认证的策略(全部成功,至少一个成功)
     */
    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);
        }
    }

    public void onLogout(PrincipalCollection principals) {
        super.onLogout(principals);
        Collection<Realm> realms = getRealms();
        if (!CollectionUtils.isEmpty(realms)) {
            for (Realm realm : realms) {
                if (realm instanceof LogoutAware) {
                    ((LogoutAware) realm).onLogout(principals);
                }
            }
        }
    }
}

SessionManager

SecurityManager provides the following interface, in addition to WebSessionManager Web environments and provides an interface to determine whether the Session Servlet container, or maintain their own Session. SecurityManager manages to create applications in all sessions of the Subject, maintenance, deletion, failure, verification work.

public interface SessionManager {
    /**
     * 启动会话
     */
    Session start(SessionContext context);
  
    Session getSession(SessionKey key) throws SessionException;
}

public interface WebSecurityManager extends SecurityManager {

    boolean isHttpSessionMode();
}

In a web environment, if the user does not take the initiative to withdraw do not know whether the session expired, so the need for regular testing session has expired, Shiro provides session authentication scheduler SessionValidationScheduler to do it. SecurityManager implementation class typically implements the interface.

Shiro provides three default SessionManager implementation:

  • DefaultSessionManager: DefaultSecurityManager use the default implementation for the environment JavaSE

  • ServletContainerSessionManager: DefaultWebSecurityManager default implementation used for the Web environment, which was used directly session Servlet Container;

  • DefaultWebSessionManager: for implementing Web environment, can replace ServletContainerSessionManager, it maintains its own session, session management directly abandoned Servlet container.

Session

Session connection relation is maintained when users access applications in multiple application interaction can identify who the user is currently accessing, and some data can be stored in multiple interaction. After such a successful login to access some sites, the site can remember user, and before the exit can identify who the current user Yes. Shiro's session support not only can be used in ordinary JavaSE applications, it can also be used in JavaEE applications, such as web applications. And use the same.

public interface Session {

    Serializable getId();

    Date getStartTimestamp();

    Date getLastAccessTime();

    long getTimeout() throws InvalidSessionException;

    void setTimeout(long maxIdleTimeInMillis) throws InvalidSessionException;

    String getHost();

    /**
     * 如果是 JavaSE 应用需要自己定期调用 session.touch()去更新最后访问时间;
     * 如果是 Web 应用,每次进入 ShiroFilter 都会自动调用 session.touch()来更新最后访问时间
     */
    void touch() throws InvalidSessionException;

    /**
     * 当Subject.logout()时会自动调用 stop 方法来销毁会话
     */
    void stop() throws InvalidSessionException;

    Collection<Object> getAttributeKeys() throws InvalidSessionException;

    Object getAttribute(Object key) throws InvalidSessionException;
 
    void setAttribute(Object key, Object value) throws InvalidSessionException;

    Object removeAttribute(Object key) throws InvalidSessionException;
}

Session provides the listener SessionListener, for monitoring session creation, overdue and stop events, If you want to listen for a particular event can be inherited SessionListenerAdapter achieved.

Shiro provide CRUD operations for SessionDAO session, AbstractSessionDAO provides the basis SessionDAO implementation, such as generating a session ID and the like; CachingSessionDAO Providing transparent to the developer session cache function is provided only to the appropriate CacheManager; MemorySessionDAO directly in memory in a session maintenance; and EnterpriseCacheSessionDAO session caching functionality provides maintenance, but the methods are empty, we need to inherit the implementation of these methods.

to sum up

Shiro integration with the Spring, many extensions for Shiro, we need to inherit the original class, and then modify the default implementation. For example, in a Web environment can implement your own Session management, you need to call setSessionManager () method in the SecurityManager, modify the default SessionManager.

There are encountered in the integration of Spring and Shiro a problem UserRealm inject IUserService, resulting in IUserService of AOP failure (such as the failure cause the transaction), but the identification of the original, you can not inject the solution instead SpringContextHolder.getBean(IUserService.class)of the way.

Guess you like

Origin www.cnblogs.com/fzsyw/p/11443740.html