Shiro框架源码学习笔记

介绍

shiro官方文档阅读。

认证

认证过程就是提供凭据证明你是谁。

术语

Subject(对象)

代表安全特定的用户。这个用户可以是人类,第三方进程,服务器等等。

Principals(主体)

代表Subject的认证属性。姓名,用户名,安全号码等等

Credentials(凭据)

用于证明用户身份的私密数据。密码,x509证书,生物识别数据等等。

Realms

特定的DAO(Data access object),用于访问数据源。

If you have usernames and password in LDAP, then you would have an LDAP Realm that would communicate with LDAP.

如何使用Shiro的认证

分为三步:

  1. 手机认证主体和凭据
  2. 提交认证主体和凭据到认证系统
  3. 允许访问,重新认证,或阻止访问

1. 手机认证主体和凭据

//封装用户名和密码
UsernamePasswordToken token = new UsernamePasswordToken( username, password );

//内置的`Remember Me`
token.setRememberMe(true);

meRemember Me如何实现的?

UsernamePasswordToken是最常用的用户名/密码认证凭证。

Shiro不管用户名/密码是如何获取的,如通过web表单,Http header或者命令行等等。

通过设置setRememberMe(true)让应用记住当前用户。

2. 提交认证主体和凭据到认证系统

//获取当前用户
Subject currentUser = SecurityUtils.getSubject();

//对当前用户执行认证
//将token传入登录方法
currentUser.login(token);

首先获取当前用户。SecurityUtils.getSubject()用于获取当前用户。在未完成认证前,当前用户都是匿名的(anonymous),没有关于它的认证信息。

然后,通过调用Subject#login()提交token完成认证。

Shiro中使用Realm表示特定的认证系统。

me:Realm表示一种认证方式,可以是LDAP,Redis,File,DB等等。

Shiro Realm Guide

3. 允许访问,重新认证,或阻止访问

如果认证通过,Subject会关联用户的信息。从这之后,因为因为有Remember Me,用户可以使用应用并从会话重新获取身份信息或者保持信息更长的时间。

如果认证失败,Shiro会抛出异常。每种认证失败方式会对应一种AuthenticationException的子类。

//登录用户,登录后通过后,currentUser.isAuthenticated()会返回true
//如果认证未通过,通过特定类型的异常,表示一种认证错误
try {
    currentUser.login(token);
} catch (UnknownAccountException uae) {
    log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
    log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
    log.info("The account for username " + token.getPrincipal() + " is locked.  " +
            "Please contact your administrator to unlock it.");
}
// ... catch more exceptions here (maybe custom ones specific to your application?
catch (AuthenticationException ae) {
    //unexpected condition?  error?
}

“Remember Me Support”

Shiro支持两种方法:isRemembered()isAuthenticated()

一个记住的(Remembered)对象有身份信息,在上一次会话认证成功后,他的主体被记住。

一个已认证的(Authenticated)对象在当前会话已经证明了他的身份。

Remembered vs Authenticated

在shiro中,Remembered对象和Authenticated对象是不同的。

Remembered能告诉系统,你可能是谁,

Authenticated是向系统证明你是谁,

因此,Authenticated会针对于敏感层度高的操作,如财务相关的信息,而Remembered主要针对敏感度不那么高的操作,如推送用户感兴趣的内容。

me:文档中以Amazon.com举例。其实淘宝也是一样的,记住功能,可以做有限的操作;与私密相关性大的操作都需要登录系统,即完成认证。

登出

currentUser.logout();

当你调用Subject#logout(),Shiro会关闭会话,并且从对象移除任何关联的身份。如果在Web应用中使用了Remember Me,调用了Subject#logout(),默认会删除浏览器的Remeber Me cookie。

授权

授权是对资源的访问控制。你可以访问哪些资源,不可以访问哪些资源。

Examples of authorization checks are: Is the user allowed to look at this webpage, edit this data, view this button, or print to this printer? Those are all decisions determining what a user has access to.

授权三要素

在Shiro中,授权主要涉及三个要素:

  • 权限
  • 角色
  • 用户

权限

表示对于某个资源可以执行什么操作。

权限是最原子级别的安全策略。

好的权限格式由两部分组成:资源类型和操作

权限的粒度

Shiro提供三个粒度的权限级别:

  • Resource Level:一个用户可以指定的资源,但是并没有特定资源的示例。
  • Instance Level:一个用户可以指定资源的实例。
  • Attribute Level:一个用户可以指定的资源实例的属性。

me:不太理解,Resource Level->Instance Level->Attribute Level粒度越细。

角色

角色是权限的集合,用来简化权限的分配。

Shiro支持两类权限:

  • 隐式的角色
  • 显式的角色
隐式的角色

隐式的角色就是不将角色显示的分配给用户,一个用户就代表着其由一个隐式的角色(这个隐式的角色会有一组权限,但是这样的一组权限显然也是没有数据存储的,那么其实数据库存储的只有用户信息,没有角色和权限的信息),针对这个隐式的角色,可以做什么,不可以做什么。这种方式,权限的控制粒度作用于角色,而不是权限,代码的维护和管理很复杂。

显式的角色(推荐)

一个显式的角色会被显式地分配一组权限。代码中的权限检查是针对权限的,而不是角色,这样,可以通过非编码,而是修改配置文件的方式,维护和管理一个角色的权限。

用户

一般来说用户是只一个应用程序中的人类。Shiro中使用Subject(对象)的概念表示一个用户,是因为对象可以表示一切和当前应用交互的实体。

用户可以和角色关联,也可以直接关联一组权限。

Shiro如何执行授权

Shiro提供的授权方式有:

  • 编程式:可以通过Java代码的if-else完成授权检查

  • 注解式:可以在Java方法上添加Java注解的方式

  • JSP/GSP标签库(TagLibs):基于角色和权限控制jsp或gsp页面的输出

  • Caching Authorization

编程式授权

角色检查
//get the current Subject 
Subject currentUser = SecurityUtils.getSubject();

if (currentUser.hasRole("administrator")) {
    //show a special button
} else {
    //don’t show the button?)
}

以上为基于角色的权限检查。缺点很明显,如果要修改一个角色的关联权限,就需要修改源代码。

What if you just want to add, remove, or redefine a role later? You’ll have to crack open your source code and change all your role checks to reflect the change in your security model. You’ll have to shut down the application, crack open the code, test it, and then restart it everytime.

me:项目的可维护性非常的差。

权限检查

Permission API

基于权限的检查是较为推荐的方式。

官方提供了两种权限检查的写法:

  • 实现Permission接口的方式
  • 使用String表示一种权限的方式
实现Permission接口的方式
//1.获取当前用户
Subject currentUser = SecurityUtils.getSubject();

//2.构造一个`Permission`对象
//含义:action=print,operation=aoe
Permission printPermission = new PrinterPermission("aoe","print");

//3.执行权限检查
if(currentUser.isPermmitted(printPermission)){
    //do one thing(show the print button?)
}else {
    //don't show the button
}
使用String表示一种权限的方式

基于String的方式,使用字符串表示一个权限。

//定义权限
//role:printer
//action:print
//resource:aoe
String perm = "printer:print:aoe";

if(currentUser.isPermitted(perm)){
    //show the print button?
} else {
    //don’t show the button?
}

以上的权限语法基于WildCardPermissions。开发人员可以开发自定义的权限字符串,只要Realm可以识别(Realm应该也要自定义)。

总结
  • 基于String的方式:简单,功能较为受限
  • 基于Permission的方式:功能全面,需要实现Permission

(**)注解式授权

Shiro提供了一组Java注解,用于基于注解的授权。这些注解用于注释方法。

启用注解的支持

在使用注解授权前,需要启用AOP的支持。

For AspectJ, you can review our AspectJ sample application.

For Spring, you can look into our Spring Integration documentation.

For Guice, you can look into our Guice Integration documentation.

基于注解的权限检查

me:虽然官方说可以使用Permission对象,但是@RequiresPermissions好像并不支持

//如果当前用户具有`create account`的权限,就授权通过
//否则,抛出AuthorizationException
@RequiresPermissions("account:create")
public void createAccount(Account acct){
    //create the account
}
基于注解的角色检查
//如果用户具有`admin`角色,就可以授权通过
//否则,抛出`AuthorizationException`
@RequiresRoles("admin")
public void createAccount(Account acct){
    //do something...(create account?)
}

JSP标签库授权

用于基于JSP/GSP的Web应用。

Shiro整合其它框架

Shiro整合Spring MVC

ShiroFilterFactoryBean

实现了FactoryBean接口。用于在基于Spring的Web应用中定义主要的Shiro过滤器。

在web.xml中需要声明一个DelegatingFilterProxy,用于匹配对应bean的id的过滤器名称

<filter>
	<filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
        <param-name>targetFilterLifeCycle</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

在spring的XML配置文件中定义:

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
	<property name="securityManager" ref="securityManager"/>
</bean>

自动发现过滤器

Shiro是通过过滤器链的方式将多个过滤器串连在一起。

filterChainDefinitions属性中,配置bean id,从而添加对应的过滤器bean

<!-- 自定义的过滤器 --> 
<bean id="myCustomFilter" class="com.class.that.implements.javax.servlet.Filter"/>
 ...
 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    ...
    <property name="filterChainDefinitions">
        <value>
            <!-- 路径映射到对应的认证过滤器 -->
            /some/path/** = authc, myCustomFilter
        </value>
    </property>
 </bean>

全局属性值

Shiro提供了 AccessControllerFilterAuthenticationFitlerAuthorizationFilter的子类。

这三个过滤器都提供了如下方法:

为了简化配置,ShiroFilterFactoryBean提供了这三个方法,从而实现过滤器链中的过滤器的默认配置。即只要在ShiroFilterFactoryBean中完成方法的配置,这些配置就会被应用到其内部定义的过滤器链中的过滤器。

DefaultFilter

枚举类型,定义了每个过滤器的默认命名

	anon(AnonymousFilter.class),
    authc(FormAuthenticationFilter.class),
    authcBasic(BasicHttpAuthenticationFilter.class),
    authcBearer(BearerHttpAuthenticationFilter.class),
    logout(LogoutFilter.class),
    noSessionCreation(NoSessionCreationFilter.class),
    perms(PermissionsAuthorizationFilter.class),
    port(PortFilter.class),
    rest(HttpMethodPermissionFilter.class),
    roles(RolesAuthorizationFilter.class),
    ssl(SslFilter.class),
    user(UserFilter.class),
    invalidRequest(InvalidRequestFilter.class);

SecurityManager

SecurityManager执行所有和安全相关的操作。认证、授权、会话管理。实现了AuthenticatorAuthorizerSessionManager接口。

大多数程序员很少会和SecurityManager

DefaultSecurityManager

//模板方法,完成登录的整体逻辑
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException ;
//登录失败,执行
protected void onFailedLogin(AuthenticationToken token, AuthenticationException ae, Subject subject) ;
//登录成功,执行
protected void onSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info, Subject subject) 

DefaultWebSecurityManager

WebSecurityManager的默认实现,用于基于Web的应用,或者任何和HTTP连接相关的应用(SOAP,http远程等)。

Shiro各重要组件

Filter

ProxiedFilterChain

过滤器链,每个请求会涉及到的过滤器,都保存在filters中。

	public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        if (this.filters == null || this.filters.size() == this.index) {
            //we've reached the end of the wrapped chain, so invoke the original one:
            if (log.isTraceEnabled()) {
                log.trace("Invoking original filter chain.");
            }
            this.orig.doFilter(request, response);
        } else {
            if (log.isTraceEnabled()) {
                log.trace("Invoking wrapped filter at index [" + this.index + "]");
            }
            //依次调用filters中的过滤器
            this.filters.get(this.index++).doFilter(request, response, this);
        }
    }

AdviceFilter

A Servlet Filter that enables AOP-style “around” advice for a ServletRequest via preHandle, postHandle, and afterCompletion hooks.

对请求实现环绕式切面增强式的处理。

	public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        Exception exception = null;

        try {
			
            // 预先处理请求是否符合要求,不符合要求(continueChain==false)就不会执行后续的过滤器链
            boolean continueChain = preHandle(request, response);
            if (log.isTraceEnabled()) {
                log.trace("Invoked preHandle method.  Continuing chain?: [" + continueChain + "]");
            }

            //如果continueChain==true,则调用过滤器链,执行后续的过滤器
            if (continueChain) {
                executeChain(request, response, chain);
            }
			
            //后置处理
            postHandle(request, response);
            if (log.isTraceEnabled()) {
                log.trace("Successfully invoked postHandle method");
            }

        } catch (Exception e) {
            exception = e;
        } finally {
            //做一些清理工作
            cleanup(request, response, exception);
        }
    }

	//(\*\*)该方法调用ProxiedFilterChain.doFilter(),从而实现ProxiedFilterChain中的过滤器都被调用到。
	protected void executeChain(ServletRequest request, ServletResponse response, FilterChain chain) throws Exception {
        chain.doFilter(request, response);
    }

	//预处理请求,如果允许其它过滤器继续处理,则返回true
	protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception;
	
	//后置处理,如果抛出异常,如果excuteChain()抛出异常,则不会执行
	protected void postHandle(ServletRequest request, ServletResponse response) throws Exception;

	//在finally代码块的cleanup()中被执行,做最后的处理,可以用于资源的释放
	public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception;

PathMatchingFilter

主要对路径做匹配,过滤

	protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
		
        if (this.appliedPaths == null || this.appliedPaths.isEmpty()) {
            if (log.isTraceEnabled()) {
                log.trace("appliedPaths property is null or empty.  This Filter will passthrough immediately.");
            }
            return true;
        }
        
		//对属于当前Filter的所有路径做过滤
        for (String path : this.appliedPaths.keySet()) {
            // If the path does match, then pass on to the subclass implementation for specific checks
            //(first match 'wins'):
            //路径匹配成功
            if (pathsMatch(path, request)) {
                log.trace("Current requestURI matches pattern '{}'.  Determining filter chain execution...", path);
                Object config = this.appliedPaths.get(path);
                //
                return isFilterChainContinued(request, response, path, config);
            }
        }

        //no path matched, allow the request to go through:
        return true;
    }

	private boolean isFilterChainContinued(ServletRequest request, ServletResponse response,
                                           String path, Object pathConfig) throws Exception {

        if (isEnabled(request, response, path, pathConfig)) { //isEnabled check added in 1.2
            if (log.isTraceEnabled()) {
                log.trace("Filter '{}' is enabled for the current request under path '{}' with config [{}].  " +
                        "Delegating to subclass implementation for 'onPreHandle' check.",
                        new Object[]{getName(), path, pathConfig});
            }
            //The filter is enabled for this specific request, so delegate to subclass implementations
            //so they can decide if the request should continue through the chain or not:
            //(\*\*)子类通过实现该方法,完成请求的预处理逻辑
            return onPreHandle(request, response, pathConfig);
        }

        if (log.isTraceEnabled()) {
            log.trace("Filter '{}' is disabled for the current request under path '{}' with config [{}].  " +
                    "The next element in the FilterChain will be called immediately.",
                    new Object[]{getName(), path, pathConfig});
        }
        //This filter is disabled for this specific request,
        //return 'true' immediately to indicate that the filter will not process the request
        //and let the request/response to continue through the filter chain:
        return true;
    }

(**)AccessControlFilter

用于判断用户是否已经登录的过滤器。

    //用于认证失败,跳转的登录页URL
    private String loginUrl;

    // Returns true if isAccessAllowed(Request,Response,Object), otherwise returns the result of onAccessDenied(Request,Response,Object).
    public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception{
        //(\*\*)先验证当前请求是否可以访问;不能访问就执行onAccessDenied策略
        return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
    }
    // 如果当前请求可以通过当前过滤器,返回true
    protected abstract boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue);
    // 处理被isAccessAllowed()拒绝的请求,如返回登录页面,又或者对登录请求做处理等等
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue);

AuthenticationFilter

	//(\*\*)获取Subject,验证其是否已经认证通过
	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        //获取Subject, 其保存了用户是否认证的信息
        Subject subject = getSubject(request, response);
        return subject.isAuthenticated() && subject.getPrincipal() != null;
    }

Subject存储了用户的相关信息,如果用户未认证,Subject#isAuthenticated()返回false

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LDWnHRlP-1624456292016)(https://raw.githubusercontent.com/Beyond14/photo_repository/master/20210621170404.png)]

AuthenticatingFilter

认证过滤器。

	//执行登录请求
	protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        // 创建用于认证的AuthenticationToken
        AuthenticationToken token = createToken(request, response);
        if (token == null) {
            String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +
                    "must be created in order to execute a login attempt.";
            throw new IllegalStateException(msg);
        }
        try {
            //(\*\*)获取用户主体,用于认证
            Subject subject = getSubject(request, response);
            //对用户请求做认证
            subject.login(token);
            //登录成功后的处理
            return onLoginSuccess(token, subject, request, response);
        } catch (AuthenticationException e) {
            return onLoginFailure(token, e, request, response);
        }
    }

FormAuthenticationFilter

	//重写了AccessControlFilter的onAccessDenied()
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        //判断是否是登录请求
        if (isLoginRequest(request, response)) {
            //如果request为POST请求就返回true
            if (isLoginSubmission(request, response)) {
                if (log.isTraceEnabled()) {
                    log.trace("Login submission detected.  Attempting to execute login.");
                }
                //执行登录,调用的是AuthenticatingFilter#executeLogin()
                return executeLogin(request, response);
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("Login page view.");
                }
                //allow them to see the login page ;)
                return true;
            }
        } else {
            if (log.isTraceEnabled()) {
                log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +
                        "Authentication url [" + getLoginUrl() + "]");
            }
			//不是登录请求,保存URL,页面跳转到登录页
            saveRequestAndRedirectToLogin(request, response);
            return false;
        }
    }

RolesAuthorizationFilter

对用户的角色进行判断。

//对用户的角色做判断,用户的角色和资源所需的角色相符,则返回true
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue);

InvalidRequestFilter

该过滤器主要是对于恶意请求做过滤,如一些违法字符等等。

UserFilter

已认证的主体(Principal)会成功访问资源,否则重定向到登录页面(loginUrl)。

	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (isLoginRequest(request, response)) {
            return true;
        } else {
            //获取授权主体
            Subject subject = getSubject(request, response);
            //查看principal是否存在,不存在说明用户未认证
            // If principal is not null, then the user is known and should be allowed access.
            return subject.getPrincipal() != null;
        }
    }

	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		//保存请求并重定向到登录页面
        saveRequestAndRedirectToLogin(request, response);
        return false;
    }

LogoutFilter

upon receiving a request, will immediately log-out the currently executing subject and then redirect them to a configured redirectUrl.

就是完成登出功能,过滤器很简单。

Subject

用于认证和授权的实体。

WebDelegatingSubject

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SLAnISdL-1624456292018)(https://raw.githubusercontent.com/Beyond14/photo_repository/master/20210618105241.png)]

未认证通过的WebDelegatingSubject,principals==nullauthenticated==false

认证通过的WebDelegatingSubject则显示如上。

Realm

Realm是一个安全组件,用于访问应用指定的安全实体(如用户、角色和权限)去决定认证和授权操作。

Realm的实现常常对应一种数据源,如DB,文件系统等。其内部访问数据源的API可以是JDBC,File IO,Hibernate或者JPA等等。

Shiro允许应用去实现这个接口去访问特定环境的数据源和数据模型对象。Realm的实现类可以通过配置插入到应用中。

ModularRealmAuthenticator

	protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        assertRealmsConfigured();
        Collection<Realm> realms = getRealms();
        //如果就一个realm执行单realm认证
        if (realms.size() == 1) {
            return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
        } else {
            //否则,执行多个Realm的认证
            return doMultiRealmAuthentication(realms, authenticationToken);
        }
    }
	
	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 {
                    //执行认证获得AuthenticationInfo
                    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;
    }

AuthenticatingRealm

支持认证Realm实现,认证行为由子类去实现。AuthenticatingRealm作为各种认证的Realm的父类,子类的认证逻辑通过重写doGetAuthenticationInfo()实现

 	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) {
            //将用户提供的AuthenticationToken和数据源中获取的AuthenticationInfo做比对
            assertCredentialsMatch(token, info);
        } else {
            log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
        }

        return info;
    }
//用于实现特定的认证逻辑
protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;
//校验用户凭据
//AuthenticationToken存储的是浏览器提供的信息,AuthenticationInfo存储的是Realm获取到的信息
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info);

AuthorizingRealm

AuthorizingRealm继承了AuthenticatingRealm,在其上添加了授权的支持。

SimpleAccountRealm

	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //强转为UsernamePasswordToken会不会抛出强转异常?
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        //返回一个AuthenticationInfo的实现类
        SimpleAccount account = getUser(upToken.getUsername());

        //对account失效的情况做判断,并抛出异常
        if (account != null) {

            if (account.isLocked()) {
                throw new LockedAccountException("Account [" + account + "] is locked.");
            }
            if (account.isCredentialsExpired()) {
                String msg = "The credentials for account [" + account + "] are expired";
                throw new ExpiredCredentialsException(msg);
            }

        }

        return account;
    }

参考

Shiro Authentication

Shiro Authorization

(**)Shiro Reference Documentation

Apache Shiro + Spring Web Example

Shiro API文档

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值