shiro 框架学习笔记-未完结

说明

该文档中的所有代码为 class 文件 反编译后的源码,与真实源码有出入,仅供大概理解。

shiro 中的重要对象解释

框架

  • Subject:主体,可以看到主体是任何可以和应用交互的 “用户”

  • SecurityManager:安全管理器,相当于springMVC中的DispatcherServlet,所有具体的交互都通过 SecurityManager 来进行控制;它管理所有的 Subject、并且负责认证和授权、及会话、缓存的管理

  • Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 shiro默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算是认证通过了;

  • Authorizer:授权器, 或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的那些功能;

  • Relams:域,可以有一个或多个Relams,可以认为是实体安全数据源,即用于获取安全实体的;可以是JDBC实现,或者是LDAP实现,或者内存实现等等。注意:shiro 不知道你的用户/权限存储在哪及以何种格式存储,所以我们一般需要在应用中实现自己的Relams。

  • SessionManager:大家应该都知道session这个概念,session需要有人管理它的生命周期,这个组件就是SessionManager。而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所以呢,Shiro 就抽象了一个自己的 Session 来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台 Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器) <不是很清楚这个>

  • SessionDao:Dao大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库中,那么大家可以实现自己的SessionDao,通过JDBC写入到数据库;比如想把session放到Memcached中,实现自己的Memcached SessionDao;另外SessionDao中可以使用Cache进行缓存,提高性能。

  • CacheManager:缓存控制器,用来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中可以提高访问的性能。

  • Cryptography:密码模块,Shiro提供了一些常见的加密组件,用于密码加密/解密的。

官方文档

官方文档-Core Concepts: Subject, SecurityManager, and Realms

Subject

Subject:用户,Subject 只是一个安全术语,它真实的含义其实就是 User ,一旦获取了Subject 对象,立刻会拥有在shiro中几乎所有的功能,例如登陆、注销、访问他们的会话、执行授权检查等等;这里的关键点是 Shiro 的 API 在很大程度上是直观的,因为它反映了开发人员考虑“每用户”安全控制的自然倾向。在代码 中的任何位置访问 Subject 也很容易,从而允许在任何需要的地方进行安全操作。

public interface Subject {
    Object getPrincipal();		// 返回当前subject的唯一标识,判断subject是否登陆

    PrincipalCollection getPrincipals();	// 和 getPrincipal() 一样,返回的是PrincipalCollection对象

  	/**
  	* 下面四个方法,是用来判断权限的
  	*/
    boolean isPermitted(String var1);

    boolean isPermitted(Permission var1);

    boolean[] isPermitted(String... var1);

    boolean[] isPermitted(List<Permission> var1);

    boolean isPermittedAll(String... var1);

    boolean isPermittedAll(Collection<Permission> var1);

    void checkPermission(String var1) throws AuthorizationException;

    void checkPermission(Permission var1) throws AuthorizationException;

    void checkPermissions(String... var1) throws AuthorizationException;

    void checkPermissions(Collection<Permission> var1) throws AuthorizationException;

    boolean hasRole(String var1);

    boolean[] hasRoles(List<String> var1);

    boolean hasAllRoles(Collection<String> var1);

    void checkRole(String var1) throws AuthorizationException;

    void checkRoles(Collection<String> var1) throws AuthorizationException;

    void checkRoles(String... var1) throws AuthorizationException;

    void login(AuthenticationToken var1) throws AuthenticationException;	// 用户登陆

    boolean isAuthenticated();

    boolean isRemembered();

    Session getSession();

    Session getSession(boolean var1);

    void logout();	// 用户登出

    <V> V execute(Callable<V> var1) throws ExecutionException;

    void execute(Runnable var1);

    <V> Callable<V> associateWith(Callable<V> var1);

    Runnable associateWith(Runnable var1);

    void runAs(PrincipalCollection var1) throws NullPointerException, IllegalStateException;

    boolean isRunAs();

    PrincipalCollection getPreviousPrincipals();

    PrincipalCollection releaseRunAs();

    public static class Builder {
        private final SubjectContext subjectContext;
        private final SecurityManager securityManager;

        public Builder() {
            this(SecurityUtils.getSecurityManager());
        }

        public Builder(SecurityManager securityManager) {
            if (securityManager == null) {
                throw new NullPointerException("SecurityManager method argument cannot be null.");
            } else {
                this.securityManager = securityManager;
                this.subjectContext = this.newSubjectContextInstance();
                if (this.subjectContext == null) {
                    throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' cannot be null.");
                } else {
                    this.subjectContext.setSecurityManager(securityManager);
                }
            }
        }

        protected SubjectContext newSubjectContextInstance() {
            return new DefaultSubjectContext();
        }

        protected SubjectContext getSubjectContext() {
            return this.subjectContext;
        }

        public Subject.Builder sessionId(Serializable sessionId) {
            if (sessionId != null) {
                this.subjectContext.setSessionId(sessionId);
            }

            return this;
        }

        public Subject.Builder host(String host) {
            if (StringUtils.hasText(host)) {
                this.subjectContext.setHost(host);
            }

            return this;
        }

        public Subject.Builder session(Session session) {
            if (session != null) {
                this.subjectContext.setSession(session);
            }

            return this;
        }

        public Subject.Builder principals(PrincipalCollection principals) {
            if (principals != null && !principals.isEmpty()) {
                this.subjectContext.setPrincipals(principals);
            }

            return this;
        }

        public Subject.Builder sessionCreationEnabled(boolean enabled) {
            this.subjectContext.setSessionCreationEnabled(enabled);
            return this;
        }

        public Subject.Builder authenticated(boolean authenticated) {
            this.subjectContext.setAuthenticated(authenticated);
            return this;
        }

        public Subject.Builder contextAttribute(String attributeKey, Object attributeValue) {
            if (attributeKey == null) {
                String msg = "Subject context map key cannot be null.";
                throw new IllegalArgumentException(msg);
            } else {
                if (attributeValue == null) {
                    this.subjectContext.remove(attributeKey);
                } else {
                    this.subjectContext.put(attributeKey, attributeValue);
                }

                return this;
            }
        }

        public Subject buildSubject() {
            return this.securityManager.createSubject(this.subjectContext);
        }
    }
}

SecurityManager

SecurityManager: 安全管理器,即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有的 Subject ;可以看出它是Shiro 的核心,它负责与其他组件进行交互。

Realm

Realm:,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

public interface Realm {
    String getName();	// 返回一个唯一的 Realm 名字

    boolean supports(AuthenticationToken var1);	// 判断此 Realm 是否支持此 Token

  	// 根据 Token 获取认证信息
    AuthenticationInfo getAuthenticationInfo(AuthenticationToken var1) throws AuthenticationException;
}

源码阅读

说明

该文档中的所有代码为 class 文件 反编译后的源码,与真实源码有出入,仅供大概理解。

一、Shiro 登陆流程

1、登陆代码入口
@Override
public String login(LoginReqBody reqBody) {
    this.verify(reqBody);
    UsernamePasswordToken token = new UsernamePasswordToken(reqBody.getUsername(), reqBody.getPassword());
    Subject subject = SecurityUtils.getSubject();		// 创建一个用户对象
    try {
        subject.login(token);			// <-----------   入口
    } catch (AuthenticationException e) {
        throw new BusinessException("用户名或密码错误", e);
    }

    return this.getToken(reqBody.getUsername(), reqBody.getTerminal());
}
2、打开 Subject (用户) 的 login() 实现
void login(AuthenticationToken var1) throws AuthenticationException;

DelegatingSubject (授权用户) 的 login() 方法

public void login(AuthenticationToken token) throws AuthenticationException {
    this.clearRunAsIdentitiesInternal();
  	// 调用 SecurityManager 的 login() 方法
    Subject subject = this.securityManager.login(this, token);
    String host = null;
    PrincipalCollection principals;
    if (subject instanceof DelegatingSubject) {
        DelegatingSubject delegating = (DelegatingSubject)subject;
        principals = delegating.principals;
        host = delegating.host;
    } else {
        principals = subject.getPrincipals();
    }

    if (principals != null && !principals.isEmpty()) {
        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 = this.decorate(session);
        } else {
            this.session = null;
        }

    } else {
        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);
    }
}
3、 打开 SecurityManager (安全管理) 的 login() 的实现
Subject login(Subject var1, AuthenticationToken var2) throws AuthenticationException;

DefaultSecurityManager (默认安全管理策略) 的 login() 方法

public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
    AuthenticationInfo info;
    try {
      	// 调用当前类的认证方法
        info = this.authenticate(token);
    } catch (AuthenticationException var7) {
        AuthenticationException ae = var7;

        try {
            this.onFailedLogin(token, ae, subject);
        } catch (Exception var6) {
            if (log.isInfoEnabled()) {
                log.info("onFailedLogin method threw an exception.  Logging and propagating original AuthenticationException.", var6);
            }
        }

        throw var7;
    }
		// 创建 登陆用户对象
    Subject loggedIn = this.createSubject(token, info, subject);
  	// 进行 登陆成功操作
    this.onSuccessfulLogin(token, info, loggedIn);
    return loggedIn;
}

DefaultSecurityManager (默认安全管理策略) 的 this.authenticate() 认证方法

调用 -> Authenticator (认证者) 的 authenticate() 认证方法

public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
    return this.authenticator.authenticate(token);
}
4、 打开 Authenticator (认证者) 的 authenticate() 认证方法
public interface Authenticator {    AuthenticationInfo authenticate(AuthenticationToken var1) throws AuthenticationException;}

AbstractAuthenticator (认证者抽象类) 的 authenticate() 方法

public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {    if (token == null) {        throw new IllegalArgumentException("Method argument (authentication token) cannot be null.");    } else {        log.trace("Authentication attempt received for token [{}]", token);        AuthenticationInfo info;        try {          	// 调用 抽象方法 doAuthenticate()            info = this.doAuthenticate(token);            if (info == null) {                String msg = "No account information found for authentication token [" + token + "] by this Authenticator instance.  Please check that it is configured correctly.";                throw new AuthenticationException(msg);            }        } catch (Throwable var8) {            AuthenticationException ae = null;            if (var8 instanceof AuthenticationException) {                ae = (AuthenticationException)var8;            }            if (ae == null) {                String msg = "Authentication failed for token submission [" + token + "].  Possible unexpected error? (Typical or expected login exceptions should extend from AuthenticationException).";                ae = new AuthenticationException(msg, var8);                if (log.isWarnEnabled()) {                    log.warn(msg, var8);                }            }            try {                this.notifyFailure(token, ae);            } catch (Throwable var7) {                if (log.isWarnEnabled()) {                    String msg = "Unable to send notification for failed authentication attempt - listener error?.  Please check your AuthenticationListener implementation(s).  Logging sending exception and propagating original AuthenticationException instead...";                    log.warn(msg, var7);                }            }            throw ae;        }        log.debug("Authentication successful for token [{}].  Returned account [{}]", token, info);        this.notifySuccess(token, info);        return info;    }}
5、AbstractAuthenticator (认证者抽象类) 的 doAuthenticate() 抽象方法 实现
protected abstract AuthenticationInfo doAuthenticate(AuthenticationToken var1) throws AuthenticationException;

**ModularRealmAuthenticator (模块化的域认证者) **的 doAuthenticate() 抽象方法 实现

protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {    this.assertRealmsConfigured();    Collection<Realm> realms = this.getRealms();  	// 如果域中的数据 为 1 调用 this.doSingleRealmAuthentication() 方法    return realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);}

**ModularRealmAuthenticator (模块化的域认证者) **的 doSingleRealmAuthentication() 单例域认证方法

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);    } else {        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);        } else {            return info;        }    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值