美文网首页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