shiro的初步学习

shiro 是一个权限管理的框架,之前就听过,但一直没学习,寒假在家还不开学,实在玩不下去了,来学个这玩玩

快速构建

  • 首先创建了一个springboot 工程
  • 然后加入shiro 相关依赖
  1. <dependency>
  2. <groupId>org.apache.shiro</groupId>
  3. <artifactId>shiro-spring-boot-web-starter</artifactId>
  4. <version>1.4.0</version>
  5. </dependency>
  • 创建config文件进行相关配置
    • shiro中有三个重要的东西
    • Subject:用户主体(关联SecurityManager,把操-作交给SecurityManager)
    • SecurityManager:安全管理器(关联Realm)
    • Realm:shiro连接数据库的桥梁
@Configuration
public class ShiroConfig {
    /**
     * 创建 ShiroFilterFactoryBean
     */
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        return shiroFilterFactoryBean;
    }

    /**
     * 创建DefaultWebSecurityManager
     *
     * shiro中securityManager用来管来 Realm
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        //创建管理类
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        //关联realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    /**
     * 创建Realm
     *
     *
     * 可以自定义Realm类
     */
    @Bean(name = "userRealm")
    public UserRealm getRealm(){
        return new UserRealm();
    }

}
  • 创建 realm
public class UserRealm extends AuthorizingRealm {
    /**
     * 执行授权逻辑
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权逻辑");
        return null;
    }

    /**
     * 执行认证逻辑
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证逻辑");
        return null;
    }
}

这个时候进入html页面进行访问是可以的,但是如果ShiroFilterFactoryBean没有加入@Bean注解的话,无论你url怎么写,都会跳转到login.jsp页面,如果出现这种情况,记得检查自己的配置文件。

简单的认证管理

新建user 的add.html 和 update.html
然后在 controller 里面(或者在webConfig下配置也可以)进行相关配置
在index.html中使用超链接指向 这两个文件

这个时候是可以进入的,因为我们还没在ShiroConfig中还没添加 Shiro内置的过滤器

  • 常用的过滤器
    • anon: 无需认证(登录) 就可以访问
    • authc: 必须认证才可以访问
    • user: rememberMe(记住我) 后才可以访问
    • role: 该资源必须得到角色权限才可以访问
    • perms: 该资源必须得到资源权限才可以访问
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //添加shiro的内置过滤器
        /**
         * shiro内置过滤器
         * 用来对 url 拦截
         */
        Map<String, String> filterMap = new LinkedHashMap<>();

        filterMap.put("/add","authc");
        filterMap.put("/update","authc");

        //调整登录页面,默认情况下为login.jsp
        shiroFilterFactoryBean.setLoginUrl("/login");


        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);


        return shiroFilterFactoryBean;
    }

这个时候再访问 /add.html,/update.html 就会发现自动重定向到了 login.html

如果需要登录验证的话 修改Realm

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证逻辑");


        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        User user = userService.findUser(token.getUsername());

        if (user == null){
            //如果返回 null  shiro会帮我们抛出一个 UnknownAccountException
            //也就是用户不存在
            return null;
        }

        //判断密码
        //SimpleAuthenticationInfo 是 AuthenticationInfo的子类
        return new SimpleAuthenticationInfo(user,user.getPassword(),"");
    }

简单的授权管理

有的时候我们需要对特定资源进行授权,当登录的用户有这个权限的时候才允许访问。

比如说现在有个需求是
两个账户halo和tu
halo拥有insert权限
tu拥有update权限

这个时候我们需要在过滤器对 url 添加所对应的权限

    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        Map<String, String> filterMap = new LinkedHashMap<>();

        filterMap.put("/add","authc");
        filterMap.put("/update","authc");


        //调整登录页面
        shiroFilterFactoryBean.setLoginUrl("/login");


        //授权过滤器
        filterMap.put("/add","perms[user:add]");
        filterMap.put("/update","perms[user:update]");
        //设置未授权的页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

        return shiroFilterFactoryBean;
    }

然后在Realm中编写 认证逻辑
注意:
如果你要通过 subject.getPrincipal() 来获取当前登录的用户的话,你需要在 doGetAuthenticationInfo方法中最后修改成
第一个参数就是你要返回的值
第二个参数用来验证密码

return new SimpleAuthenticationInfo(user,user.getPassword(),””);

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权逻辑");

        //给资源进行授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //对 user:add 进行授权
//        info.addStringPermission("user:add");

        //获取当前登录的用户
        //其实我觉得这步没必要,又去数据库查了,较好的做法应该在登录的时候就一次性拿出权限
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();

        info.addStringPermission(user.getPerms());
        return info;
    }

如果没有权限的话,会跳转到setUnauthorizedUrl()设置的 url