shiro 入门 测试 hello test

Shiro的架构有3个主要概念:Subject、SecurityManager和Realms. 提供的服务有Authentication(认证),Authorization(授权),Session Management(会话管理),Cryptography(加密) 建议参考 第一章 Shiro简介——《跟我学Shiro》

恰好有时间就学习下shiro,先从一个shiroHelloTest做起

测试代码

<!--pom.xml-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.4.0</version>
</dependency>
@Test
public void t(){
    //使用的jar版本是1.4.0,`IniSecurityManagerFactory` 已经弃用.
    DefaultSecurityManager securityManager = new DefaultSecurityManager();
    
    //MyShiroRealm 继承AuthorizingRealm,需要实现用于验证用户和权限的2个方法.
    securityManager.setRealm(new MyShiroRealm());
    
    //SecurityUtils存放一个全局的securityManager
    SecurityUtils.setSecurityManager(securityManager);
    
    //从ThreadLocal 中获取subject,如果没有则build一个
    Subject subject = SecurityUtils.getSubject();

    UsernamePasswordToken token = new UsernamePasswordToken("song", "123");

    try {
        subject.login(token);
    } catch (AuthenticationException e) {
        e.printStackTrace();
    }

    //看是否已验证或者说已登录
    Assert.assertEquals(true, subject.isAuthenticated());

    subject.logout();

    Assert.assertEquals(false, subject.isAuthenticated());
}

小黄鸭的看代码的方法,一行一行的看啊

0. 先附上类图

1.第一行代码对应的DefaultSecurityManager的构造方法

public DefaultSecurityManager() {
    super();
    //以下暂且不看,猜测是一些工厂类和dao的方法,用到时候再看
    this.subjectFactory = new DefaultSubjectFactory();
    this.subjectDAO = new DefaultSubjectDAO();
}

对super()查看:

//父类SessionsSecurityManager的构造方法
this.sessionManager = new DefaultSessionManager();
applyCacheManagerToSessionManager();

//爷爷AuthorizingSecurityManager的构造方法
//realm的一些实现
this.authorizer = new ModularRealmAuthorizer();

//祖爷爷AuthenticatingSecurityManager的构造方法
this.authenticator = new ModularRealmAuthenticator();

//爷爷的爷爷RealmSecurityManager的构造方法 什么也没做
//祖宗CachingSecurityManager,use a default event bus
setEventBus(new DefaultEventBus());

只知道构造方法初始化了一些默认的东西.具体干嘛的回头看

2. securityManager.setRealm(new MyShiroRealm());

securityManager.setRealm(new MyShiroRealm());

setRealm 是 默认securityManager的爷爷的爷爷 RealmSecurityManager的方法.
在执行完set的语句后,执行以下两个方法

//判断realm是否继承CacheManagerAware,是则CacheManager set realm里,CacheManager是其祖宗的字段
applyCacheManagerToRealms();
//set eventBus 同上
applyEventBusToRealms();

3. Subject subject = SecurityUtils.getSubject();进入该方法

public static Subject getSubject() {
    Subject subject = ThreadContext.getSubject();
    if (subject == null) {
        subject = (new Subject.Builder()).buildSubject();
        ThreadContext.bind(subject);
    }
    return subject;
}

Builder 是 Subject的内部类.看其构造方法

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

查看 newSubjectContextInstance() 返回 return new DefaultSubjectContext();
其父类就是一个被封装的map,通过一个map存储着一些键值对,写了一下get的方法
最终调用,好吧又回到了securityManager

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

默认的securityManager会处理下subjectContext中的session,securityManager,权限管理什么的,不过测试的时候这些都是null. 最后交给DefaultSubjectFactory处理

    public Subject createSubject(SubjectContext context) {
        SecurityManager securityManager = context.resolveSecurityManager();
        Session session = context.resolveSession();
        boolean sessionCreationEnabled = context.isSessionCreationEnabled();
        PrincipalCollection principals = context.resolvePrincipals();
        boolean authenticated = context.resolveAuthenticated();
        String host = context.resolveHost();

        return new DelegatingSubject(principals, authenticated, host, session, sessionCreationEnabled, securityManager);
    }

DelegatingSubjectsubject的实现类

5. 看subject.login(token);

通过上面的代码一番跟踪,login方法可以看DelegatingSubject的实现

public void login(AuthenticationToken token) throws AuthenticationException {
        clearRunAsIdentitiesInternal();
        
        //登录,如果失败一般会抛出异常来进行处理.
        Subject subject = securityManager.login(this, token);
        
        //-----------
        //以下基本是把登录返回的subject的内容赋值给当前的subject
        //------------
        
        PrincipalCollection principals;

        String host = null;
        
        if (subject instanceof DelegatingSubject) {
            DelegatingSubject delegating = (DelegatingSubject) subject;
            //we have to do this in case there are assumed identities - we don't want to lose the 'real' principals:
            principals = delegating.principals;
            host = delegating.host;
        } else {
            principals = subject.getPrincipals();
        }

        if (principals == null || principals.isEmpty()) {
            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);
        }
        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 = decorate(session);
        } else {
            this.session = null;
        }
    }
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
    AuthenticationInfo info;
    try {
        //长辈AuthenticatingSecurityManager中authenticator.authenticate(AuthenticationToken authenticationToken)
        //实现的方法是AbstractAuthenticator类public final AuthenticationInfo authenticate(AuthenticationToken token)
        //最后执行securityManager.setRealm(new MyShiroRealm())一步 set进去的realm 的getAuthenticationInfo方法
        //如果有多个realm则循环执行,通过实现AuthenticationStrategy.afterAllAttempts()来判断所有的认证结果是否有效
        info = authenticate(token);
    } catch (AuthenticationException ae) {
        try {
            onFailedLogin(token, ae, subject);
        } catch (Exception e) {
            if (log.isInfoEnabled()) {
                log.info("onFailedLogin method threw an " +
                        "exception.  Logging and propagating original AuthenticationException.", e);
            }
        }
        throw ae; //propagate
    }

    //生成一个新的subject,并把旧的subject,token,info 添加到其context里
    Subject loggedIn = createSubject(token, info, subject);

    //执行记住我的逻辑
    onSuccessfulLogin(token, info, loggedIn);

    return loggedIn;
}

6. logout

直接看securityManager的logout处理

  1. beforeLogout(subject); 记住我的退出
  2. 如果实现了LogoutAware接口,则执行其onLogout方法
  3. subjectDao 执行 delete subject
  4. 最后 stopSession

转载于:https://my.oschina.net/northerSong/blog/907131

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值