Shiro-SpringBoot (一)

本文介绍了如何在SpringBoot项目中集成Apache Shiro进行权限控制,包括RBAC概念解析、Shiro的集成与配置、登录认证流程,以及现有不足点的讨论,如不支持AJAX调用和前端模板元素权限控制。
摘要由CSDN通过智能技术生成

前不久负责项目中刚好也使用了Shiro做权限控制,趁着空闲期想把之前做的整理一下。在日常项目开发中,权限认证是不可少的模块。比较常用的有Spring Security,或是轻量级的Apache Shiro。相对来说Shiro提供了认证、授权、加密、会话管理、与Web集成、缓存等。这些都是日常会用到的,而且Shiro的API比较简洁,学习成本相对低。接下来将整理一下在SpringBoot中如何集成Shiro:

  • RBAC介绍
  • SpringBoot集成Shiro和配置
  • Shiro的登录和认证
  • 当前不足点

(一) RBAC介绍

**RBAC(Role-Based Access Control )**基于角色访问控制,在权限设计上用户是基于角色进行权限认证,而角色又是和资源相关联的。这样在设计和管理上简化了权限的操作,它们都是层级依赖,更方便我们的管理。如此一来,数据库表设计可以如下图:
cmd-markdown-logo

(二) SpringBoot集成Shiro和配置

1、导入Shiro依赖

pom.xml中加入以下依赖

<dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.2.5</version>
</dependency>
2、配置Shiro

在配置之前,首先了解一下Shiro中主要功能,并看看它们主要是做什么的。

  • Subject 安全视角下与软件交互的实体(用户,第三方服务等等)
  • Authenticator 用户登录时进行账户的认证
  • Authorizer 用户登录时进行账户的权限资源认证
  • Realms每当执行认证或授权时,shiro会从程序配置的一个或多个Realm中查询

新建JAVAShiroRealm用于继承ShiroAuthorizingRealm抽象类,并复写doGetAuthenticationInfodoGetAuthorizationInfo用于账户和权限的认证。

public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String userName = token.getUsername();
        String password = new String((char[])token.getCredentials());
        //这里可以自行查询数据库进行验证
        if(userName != "zhangshan" && password != DigestUtils.md5Hex("123456")) {
            throw new UnknownAccountException ("未知的账户认证失败");
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                userName, //用户名
                DigestUtils.md5Hex("123456"), //密码
                getName()  //realm name
        );
        //存入session
        //SecurityUtils.getSubject().getSession().setAttribute("user", user);
        return authenticationInfo;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String useCode = (String) principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        Map<String,Object> userAuthorityInfoMap = userService.getUserAuthorityInfo(useCode);
        //这里可以从数据库进行查询
        List<String> roles = new ArrayList<String>();
        List<String> perms = mew ArrayList<String>();
        roles.add("管理员");
        perms.add("查看用户模块");
        simpleAuthorizationInfo.addRoles(roles);
        simpleAuthorizationInfo.addStringPermissions(perms);
        return simpleAuthorizationInfo;
    }

}

SpringBoot中配置Shiro,配置URL过滤规则

@Configuration
public class ShiroConfiguration {

    /**
     * 负责org.apache.shiro.util.Initializable类型bean的生命周期的,初始化和销毁。
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * shiro密码认证配置,使用MD5 HEX16进制
     */
    @Bean(name = "hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("MD5");
        credentialsMatcher.setHashIterations(1);//散列的次数,相当于md5("");
        credentialsMatcher.setStoredCredentialsHexEncoded(true);//采用HEX16进制编码
        return credentialsMatcher;
    }

    /**
     * ShiroRealm,需要自己实现自定义的认证类,继承自AuthorizingRealm,负责用户的认证和权限的处理
     */
    @Bean(name = "shiroRealm")
    @DependsOn("lifecycleBeanPostProcessor")
    public ShiroRealm shiroRealm() {
        ShiroRealm realm = new ShiroRealm();
        //realm.setCacheManager(ehCacheManager());
        return realm;
    }

    /**
     * EhCacheManager,缓存管理,用户登陆成功后,把用户信息和权限信息缓存起来,
     * 然后每次用户请求时,放入用户的session中,如果不设置这个bean,每个请求都会查询一次数据库。
     */
//    @Bean(name = "ehCacheManager")
//    @DependsOn("lifecycleBeanPostProcessor")
//    public EhCacheManager ehCacheManager() {
//        EhCacheManager ehcacheManager = new EhCacheManager();
//        //ehcacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
//        return ehcacheManager;
//    }

    /**
     * SecurityManager权限管理,主营管理登陆,登出,权限,session的处理
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(shiroRealm());
        //securityManager.setCacheManager(ehCacheManager());
        return securityManager;
    }

    /**
     * ShiroFilterFactoryBean配置URL过滤链规则
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setSuccessUrl("/index");
        shiroFilterFactoryBean.setUnauthorizedUrl("/pages/403");
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //Shiro拦截URL规则
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/403", "anon");
        filterChainDefinitionMap.put("/userInfo/**", "authc,perms[查看用户模块]");
        filterChainDefinitionMap.put("/messageInfo/**", "authc,roles[管理员]");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 由Advisor决定对哪些类的方法进行AOP代理。
     */
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    /**
     * shiro里实现的Advisor类,内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor aASA = new AuthorizationAttributeSourceAdvisor();
        aASA.setSecurityManager(securityManager());
        return aASA;
    }

}

(三) Shiro的登录和认证

新建LoginController进行登录验证Shiro。在subject.login(token)这行代码执行的时候,Shiro会回调我们上面实现的ShiroRealm进行账户认证和权限认证。

@RequestMapping("/login")
@Controller
public class LoginController {

    private static Logger logger = LoggerFactory.getLogger(LoginController.class);

    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    public String loginPage(){
        return "login";
    }

    @RequestMapping(value = "/check",method = RequestMethod.POST)
    @ResponseBody
    public String login(@RequestParam("userName") String userName, @RequestParam("passWord") String passWord){
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(userCode, DigestUtils.md5Hex(passWord));
        try {
            subject.login(token);
        } catch (Exception e) {
            logger.error("用户用户名和密码..验证未通过");
            return "redirect:/login";
        }
        // catch (UnknownAccountException e) {
            // logger.error("未知的账户认证失败);
        // } catch (DisabledAccountException e) {
            // logger.error("账户已经被禁用,认证失败");
        // }

         return "/index";
    }

}

(四) 当前不足点

到这里Shiro已经可以进行登录验证,在配置中也可以对指定的URL拦截,并通过角色和资源认证才可以访问。但是细心想想,还是有一些不足,在一下节将对其进行优化,主要有以下几点

  • 不支持AJAX的调用,目前只能重定向页面,这对于一些前后端分离的开发是不满足的
  • 不支持前台FreeMarker模板页面元素的权限控制,包括按钮、文字等
  • URL拦截冗余在代码,虽然说SpringBoot提倡JAVA Configuration配置,但我们还是想要单独写到配置文件,例如yml或是properties
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值