美文网首页SpringHome
spring boot整合shiro之基本使用探究

spring boot整合shiro之基本使用探究

作者: 毛于晏 | 来源:发表于2018-11-26 00:31 被阅读160次

    之前写过一个springboot整合shiro的文章, 那时候只是帮shiro跑起来, 可以实现拦截, 但是出问题了完全不知道怎么去解决, 这几天一直在反复使用shiro, 就想把他的运作原理搞懂; 正所谓知己知彼百战百胜嘛; 废话不多说, 直接干起来~~!

    备注:文章末尾包含github的shiro_demo

    1.shiro.jar

    <!--shiro-->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.4.0</version>
            </dependency>
    

    2.shiro的配置类, ShiroConfig

    当然ShiroConfig这个名字看心情取, 不过重要的是这个类上面要加个注解
    @Configuration
    用springboot的人应该都知道是啥, 告诉spring这个是个配置类, 跟读取xml文件一样, 在spring加载的时候容器创建对象的时候就要读取这个类; 也就是说这个类是在spring加载的时候读取的, 这个地方很重要, 后面的话我会因为这个类的读取时机而有一些小重点;

    /**
     * @Author MaoLG
     * @Date 2018/11/17  11:24
     */
    @Configuration
    public class ShiroConfig {
    /**
         * ★必须 配置
         * 该方法是读取自定义的认证授权规则
         * 我这边实现的是 AuthorizingRealm 抽象类, 重写里面的
         * doGetAuthorizationInfo(授权) ; doGetAuthenticationInfo(认证)方法
         */
        @Bean
        public ShiroRealm shiroRealm() {
            ShiroRealm realm = new ShiroRealm();
            return realm;
        }
    
    /**
         * ★必须 配置
         * SecurityManager 安全管理器,
         *
         * @return
         */
        @Bean(name = "securityManager")
        public DefaultWebSecurityManager securityManager() {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            //将自定义的realm加入该管理器中, 当使用shiro时, 安全管理器就会去找该规则去认证授权
            securityManager.setRealm(shiroRealm());
            return securityManager;
        }
    
    /**
         * ★必须 配置
         * 该方法为shiro的过滤器, 配置过滤的规则(哪些路径需要哪些权限,就在这里配置)
         */
        @Bean(name = "shiroFilter")
        public ShiroFilterFactoryBean shiroFilterFactoryBean() throws DataAccessException {
            ShiroFilterFactoryBean shiroFilterFactoryBean = null;
            shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager());
    
            //url权限配置, url为controller设置的可访问的路径 ps:url可以使用*(通配符设置)
            Map<String, String> filterChainDefinitionManager = new LinkedHashMap<String, String>();
            //可以匿名访问的权限 anon:不登录也可访问
            filterChainDefinitionManager.put("/user/login", "anon");
    
            //必须登录的权限 authc:必须登录才能访问的权限, 配置了访问权限的可以不用再配置必须登录访问
            filterChainDefinitionManager.put("/user/add", "authc");
    
            filterChainDefinitionManager.put("/user/update", "authc");
            //退出登录 logout:退出登录, shiro帮我们实现, 该路径方法可不编写任何逻辑,可清楚shiro记录的认证
            filterChainDefinitionManager.put("/user/logout", "logout");
    
            //动态获取,  设置权限访问
           /*★这里就是上面说的小重点, 因为配置类是在spring创建对象的时候读取的, 我的权限是配置
            在数据库中(这是废话本来就要配置在数据库中)
            用mybatis来访问数据库,是不可能的(此时spring中的对象还没有全部放在容器中, @Autowired注入不了值) 这里用的是自己封装的JDBC直接访问的数据库
            */
            JDBCTemplate jdbcTemplate = new JDBCTemplate();
            String permissionSql = "select name , url from t_permission";
            List<Permission> permissions = jdbcTemplate.query(permissionSql, new PermissionMapper(), null);
            for (Permission permission: permissions){
               //当配置了perms参数的字符串就会调用授权方法, 没配置不会调用
                filterChainDefinitionManager.put(permission.getUrl(), "perms["+permission.getName()+"]");
            }
    
    
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager);
            //配置当未登录时候跳转的路径
            shiroFilterFactoryBean.setLoginUrl("/login.html");
            //配置没有权限时候访问的路径
            shiroFilterFactoryBean.setUnauthorizedUrl("/401.html");
            return shiroFilterFactoryBean;
        }
    }
    

    3.自定义规则 ShiroRealm

    /**
     * 自定义realm
     *
     * @Author MaoLG
     * @Date 2018/11/17  13:03
     */
    public class ShiroRealm extends AuthorizingRealm {
        //读取用户权限,我这里没有角色表, 具体根据自己实际需求编写
        @Autowired
        private PermissionDao permissionDao;
    
        @Autowired
        private UserDao userDao;
    
        @Autowired
        private RoleDao roleDao;
    
        /**
         * 授权
         * 该方法将用户拥有的权限和角色放入到SimpleAuthorizationInfo 对象中, 由shiro来进行权限校验
         * 访问每一个权限接口时候 都会调一次该方法, 也就意味着每次都会访问数据库, 这里需要调用一次该方法的时候将权限/角色缓存起来,
         * @param principalCollection
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            //获得登录时提供的重要凭证, 这里获得的是登录账号
            String username = (String) principalCollection.getPrimaryPrincipal();
            //查询到该账号下所有的权限d
            List<Permission> permissions = permissionDao.selectUserOwnPermission(username);
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //把用户所有权限加在SimpleAuthorizationInfo 对象中, shiro自己来读取对比权限
            for (Permission permission : permissions) {
                info.addStringPermission(permission.getName());
            }
            //如果有角色的需要, 调用info.addRole("角色"); 同权限一个道理
            //添加角色
            List<Role> roles = roleDao.selectUserOwnRole(username);
            for (Role role: roles){
                info.addRole(role.getName());
            }
            System.out.println("授权");
            return info;
        }
    
        /**
         * 认证
         * .....这个太简单了不知道怎么去描述了
         * 想到了, 当用户登录, 也就是调用ubject subject = SecurityUtils.getSubject();
         *  subject.login(token); 这个的时候就调用该方法, 
         * @param authenticationToken
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
                throws AuthenticationException {
            SimpleAuthenticationInfo info = null;
            try {
                //强转UsernamePasswordToken 类型
                UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
                //获得登录账号
                String username = token.getUsername();
    
                char[] pwd = token.getPassword();
                //获得登密码
                String password = String.valueOf(pwd);
                User user = new User();
                user.setUsername(username);
                user.setPassword(password);
                QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);
                User u = userDao.selectOne(queryWrapper);
    
                if (u == null) {
                    //账号不存在抛出该异常, controller抓取丢到前台就好了
                    throw new AuthenticationException("账号或密码错误");
                } else {
                    //shiro默认配置回去找t_user表 来查询 , 也可根据自己实际业务编写
                    info = new SimpleAuthenticationInfo(username, password, getName());
                    System.out.println("认证");
                }
            } catch (AuthenticationException e) {
                throw e;
            }
    
            return info;
        }
    }
    

    配置完着俩类, 你的shiro就可以用了, 然后我有一点疑惑, shiro的过滤器方法也提供了roles[角色名] 的过滤参数, 让我搞不太明白 我觉得角色有必要再shiro里面配置吗? 因为有role下面包含的是权限,权限已经配置拦截, 再配置role拦截好像多次一举 例如:
    filterChainDefinitionManager.put("/user/update", "roles[admin]");
    filterChainDefinitionManager.put("/user/update", "perms[update]");
    个人觉得, role只是为了便于管理权限, 而不作为拦截的标志; 如果有不同意见 欢迎留言, 最后把我自己的写的shiro的小demo贴下;
    shiro_demo,实现登录认证访问授权,内涵sql文件

    2018/11/29更新-自动登录rememberMe
              更新-ShiroRealm自定义缓存
    

    相关文章

      网友评论

        本文标题:spring boot整合shiro之基本使用探究

        本文链接:https://www.haomeiwen.com/subject/dunmqqtx.html