Shiro认证与授权源码分析,shiro认证源码


这里使用官方提供的demo进行调试,进入源码分析。

官方demo地址:https://github.com/apache/shiro/tree/master/samples/quickstart

public class Quickstart {

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


    public static void main(String[] args) {
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();

        SecurityUtils.setSecurityManager(securityManager);

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

        // 获取当前用户的会话 (不依赖于Web容器!!!)
        Session session = currentUser.getSession();
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");
        if (value.equals("aValue")) {
            log.info("Retrieved the correct value! [" + value + "]");
        }

        if (!currentUser.isAuthenticated()) {
            UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
            token.setRememberMe(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 (AuthenticationException ae) {
                //unexpected condition?  error?
            }
        }

        //打印身份标识 (这里的身份标识是用户名):
        log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

        //测试用户角色:
        if (currentUser.hasRole("schwartz")) {
            log.info("May the Schwartz be with you!");
        } else {
            log.info("Hello, mere mortal.");
        }

        //测试用户权限
        if (currentUser.isPermitted("lightsaber:wield")) {
            log.info("You may use a lightsaber ring.  Use it wisely.");
        } else {
            log.info("Sorry, lightsaber rings are for schwartz masters only.");
        }

        //测试实例级别的用户权限:
        if (currentUser.isPermitted("winnebago:drive:eagle5")) {
            log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
                    "Here are the keys - have fun!");
        } else {
            log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
        }
        currentUser.logout();

        System.exit(0);
    }
}

身份认证过程

官方文档中提供了一张身份认证的图,直接看这张图可能还不能完全掌握认证的过程。调试源码源码过后,再回头看这张图,这张图才会深深的烙印在脑海中。

1. 从Demo中的Subject.login方法开始

Subject接口实现类如下,这里demo不是Web环境,所以使用的实现类是DelegatingSubject:

2. SecurityManager.login()

SecurityManager接口的实现类

3. Authenticator认证器

认证器的实现类,SecurityMananger也继承自Authenticator,通过查看AuthenticatingSecurityManager源码其实就是Authenticator的代理。而真正实现认证功能的Authenticator实现类只有一个ModularRealmAuthenticator,从类的名字可以看出这个认证器的实现原理——模块化认证器:一个Realm就是一个认证模块。

认证器源码实现:

模块化认证器:

单模块认证

官方提供的这个例子就是单模块认证(IniRealm,用户、角色、权限等信息保存在ini配置文件中)。

单模块认证很简单,它直接调用realm.getAuthenticationInfo方法。

多模块认证

4. 多模块认证策略AuthenticationStrategy

Shiro提供了三种认证策略

AuthenticationStrategy 描述
AtLeastOneSuccessfulStrategy 如果一个(或多个)Realm认证成功,才被认为是成功的。如果没有任何一个验证成功,则认证失败。
FirstSuccessfulStrategy 只有从第一个成功验证的领域返回的信息将被使用,所有后面的Realm的认证信息将被忽略。如果没有任何一个验证成功,则认证失败。
AllSuccessfulStrategy 所有配置的Realm都必须认证成功,才能被认为是成功的。如果有任何一个认证不成功,则认证失败。

ModularRealmAuthenticator默认使用AtLeastOneSuccessfulStrategy

如果想修改认证策略可以在ini文件中配置:

[main]
...
authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy

securityManager.authenticator.authenticationStrategy = $authcStrategy

...

5. 认证模块Realm的实现

Shiro提供了如下的认证模块实现类,在官方的这个Demo中,由于使用的是Ini配置文件的方式,所以使用的Realm是IniRealm。

通常我们的用户权限信息存储在数据库中,需要我们继承AuthenticatingRealm,并重载它的doGetAuthenticationInfo方法来从数据库中获取用户身份认证信息。但是大多数情况我们会继承AuthorizingRealm,因为它不仅仅包括认证,还包括授权过程,通过重载它的doGetAuthorizationInfo方法实现授权。

授权过程

授权主要在调用Subject.hasRole或Subject.isPermitted等检查角色或权限的方法时触发。

官方也提供了一张图来描述授权的过程:

授权过程与身份认证的过程代码很类似(其实更简单),我就不在文章中具体贴图了,感兴趣的读者可以自行调试。

查看评论

相关内容

    暂无相关文章