shiro笔记(三)

起初刚接触到授权这个词我以为是将某个权限授予某个用户,后来经过网络调研发现,授权实际上是检验某个用户是否有访问某个资源的权限。

比如,普通用户想观看VIP视频会被网站阻止,因为该用户没有访问该资源的权限。

关于授权需要了解的概念:

主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)

主体:在shiro中代表用户,不一定是某个人,也许是爬虫。

资源:用户可以访问的东西。

权限:代表的是用户有没有某些操作(crud之类的)的权限。

角色:用户的角色,权限的集合。

粗粒度:以角色来授权。

细粒度:以权限来授权。

如果采用粗粒度的方式,当某个角色不再具备某个权限时,需要改动代码。


授权的三种方式:

代码授权、JSP标签授权(这个就不学了)、注解授权(这个着重看一下)。

代码授权:

Subject subject = SecurityUtils.getSubject();  
if(subject.hasRole(“角色”)) {  
    //有权限  
} else {  
    //无权限  
}  

注解授权:

@RequiresRoles("角色")  
public void check() {  
    //有权限  
} 

教程里关于这部分的代码貌似没给太多...于是,我自己发挥了一下...

首先,是代码授权,自定义realm,假装是从数据源中获取数据...

public class SelfRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //授权会调用这个方法,从数据源中获取权限列表
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addRole("admin"); //假装从数据源中获取的用户权限为admin
        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //认证方法,和上一篇笔记的一样
        String username = (String) authenticationToken.getPrincipal();
        String password = new String((char[])authenticationToken.getCredentials());
        if(!"joker".equals(username)) {
            throw new UnknownAccountException();
        }
        if(!"shiro".equals(password)) {
            throw new IncorrectCredentialsException();
        }
        return new SimpleAuthenticationInfo(username, password, getName());
    }
}

然后,修改了一下测试方法,假装用户登录后访问某个资源,然后进行判断(采用的方式是粗粒度)

public class Boot {

    @Test
    public void test() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(new SelfRealm());
        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("joker", "shiro");
        try {
            subject.login(token);
        } catch (Exception e) {}
        System.out.println("登录状态:" + subject.isAuthenticated());
        System.out.println("是否为管理员:" + subject.hasRole("admin")); //此处假装访问某资源,进行校验
    }
}

经测试发现,如果用户不登录,则授权结果一定为false,如果用户登录后登出,那么结果也是false。

授权流程:

主体(Subject)调用isPermitted(是否拥有某权限,细粒度)/hasRole(是否拥有某角色,粗粒度),会调用到(核心管理器)SecurityManager,然后会调用到授权器(Authorizer)。

后面还有一些流程,感觉记不太住,此处暂不做记录,等以后有能力阅读shiro源码再详细学习。

教程中介绍了两种授权的流程:

授权器授权、realm授权(没太搞懂怎么弄...)

还有一些高端操作,自定义授权器什么的...估计以后有能力读懂源码,就可以领会这些进阶操作,现阶段暂时略过。

目前没整合其他框架搭建一个小的项目,没想到用什么简单的方式测试注解方式授权,只好拿着上次根据网上教程搭建的练手shiro整合springboot项目改造了一下。

   @GetMapping("/role")
    @RequiresRoles("admin")
    public String role() {
        return "role success";
    }

然后惊讶地发现这个注解压根不生效,但是,如果采用代码授权的话还是可以正确执行。

于是,再一次面向网络编程,发现搜到一堆xml之类的配置,嗯... ...但是我需要的是配置类的方式进行配置,终于找到一篇文章说需要加入一个Bean。

@Bean
public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
        return new DefaultAdvisorAutoProxyCreator();
}

加入之后发现还是不生效... ...

无奈之下,看了一波xml配置,发现其中用到了AuthorizationAttributeSourceAdvisor,并且将securityManager交给了这个类的实例对象。

于是,又加入了一段代码:

   @Bean
    public static AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

这一次注解终于生效了,之前看到一篇文章说这个注解必须写在service层才行,写在controller层不管用,经实践表明写在controller层是管用的,并且还有一个意外发现,当controller和service两层都有这个注解时,

如果controller层通过授权,则即便service层也会通过,即便该用户并不具备service层注解要求的权限也一样会通过。

以下是我根据网上的文章配置的简易版shiro配置类(记录一下,省得下次再去查):

@Configuration
public class ShiroConfig {

    @Bean
    public BlogRealm newBlogRealm() {
        return new BlogRealm();
    }

    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
        return new DefaultAdvisorAutoProxyCreator();
    }

    @Bean
    public static AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
    @Bean("securityManager")
    public SecurityManager newSecurityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(newBlogRealm());
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, String> map = new HashMap<>();
        map.put("/blog/login", "anon");//登录这一行和下一行应该配置一个即可(个人猜测,暂未实践)
        shiroFilterFactoryBean.setLoginUrl("/blog/login");
        map.put("/blog/role", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }
}
原文地址:https://www.cnblogs.com/wxdmw/p/13897651.html