zoukankan      html  css  js  c++  java
  • springboot整合shiro

    请大家在看本文之前,先了解如下知识点: 
    1、Shiro 是什么?怎么用? 
    2、Cas 是什么?怎么用? 
    3、最好有Spring基础

    可以先看看这两篇文章,按照这2篇文章的内容做一遍: 
    Spring Boot Shiro 权限管理 
    CAS单点登录

    首先看一下下面这张图: 
    第一个流程是单纯使用Shiro的流程。 
    第二个流程是单纯使用Cas的流程。 
    第三个图是Shiro集成Cas后的流程。

    PS:流程图急急忙忙画的,整体上应该没有什么问题,具体细节问题还请大家留言指正。

    如果你只是打算用到你的Spring Boot项目中,那么看着如下配置完成便可。 
    如果你想进一步了解其中的细节,还是建议大家单独配置Shiro、单独配置Cas,看看官方相关文档。 
    Shiro在1.2版本开始提供了对cas的集成,按下面添加依赖到pom.xml中:

    <!--Apache Shiro所需的jar包 -->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.2.4</version>
            </dependency>
            <dependency>  
                <groupId>org.apache.shiro</groupId>  
                <artifactId>shiro-ehcache</artifactId>  
                <version>1.2.4</version>  
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-cas</artifactId>
                <version>1.2.4</version>
            </dependency>

    shiro-cas 依赖 shiro-web,shiro-web 依赖 shiro-core,所以添加shiro-cas后shiro-web.jar和shiro-core.jar会自动被引用。 
    cas被shiro集成后,其原理就是shiro将casFilter加入到shiroFilter的filterChain中。

    在SpringBoot工程中创建ShiroCasConfiguration.java

    package org.springboot.sample.config;
    
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    import javax.servlet.Filter;
    
    import org.apache.shiro.cache.ehcache.EhCacheManager;
    import org.apache.shiro.cas.CasFilter;
    import org.apache.shiro.cas.CasSubjectFactory;
    import org.apache.shiro.spring.LifecycleBeanPostProcessor;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springboot.sample.dao.IScoreDao;
    import org.springboot.sample.security.MyShiroCasRealm;
    import org.springboot.sample.service.StudentService;
    import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    import org.springframework.boot.context.embedded.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.filter.DelegatingFilterProxy;
    
    /** * Shiro集成Cas配置 * * @author 单红宇(365384722) * @myblog http://blog.csdn.net/catoop/ * @create 2016年1月17日 */
    @Configuration
    public class ShiroCasConfiguration {
    
        private static final Logger logger = LoggerFactory.getLogger(ShiroCasConfiguration.class);
    
        // CasServerUrlPrefix
        public static final String casServerUrlPrefix = "https://localhost:8443/cas";
        // Cas登录页面地址
        public static final String casLoginUrl = casServerUrlPrefix + "/login";
        // Cas登出页面地址
        public static final String casLogoutUrl = casServerUrlPrefix + "/logout";
        // 当前工程对外提供的服务地址
        public static final String shiroServerUrlPrefix = "http://localhost:9090/myspringboot";
        // casFilter UrlPattern
        public static final String casFilterUrlPattern = "/shiro-cas";
        // 登录地址
        public static final String loginUrl = casLoginUrl + "?service=" + shiroServerUrlPrefix + casFilterUrlPattern;
    
        public EhCacheManager getEhCacheManager() {  
            EhCacheManager em = new EhCacheManager();  
            em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");  
            return em;  
        }  
    
        /** * 注册DelegatingFilterProxy(Shiro) * * @param dispatcherServlet * @return * @author SHANHY * @create 2016年1月13日 */
        @Bean
        public FilterRegistrationBean filterRegistrationBean() {
            FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
            filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
            // 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 
            filterRegistration.addInitParameter("targetFilterLifecycle", "true");
            filterRegistration.setEnabled(true);
            filterRegistration.addUrlPatterns("/*");
            return filterRegistration;
        }
    
        @Bean(name = "lifecycleBeanPostProcessor")
        public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
        @Bean
        public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
            DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
            daap.setProxyTargetClass(true);
            return daap;
        }
    
        @Bean(name = "securityManager")
        public DefaultWebSecurityManager getDefaultWebSecurityManager(MyShiroCasRealm myShiroCasRealm) {
            DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
            dwsm.setRealm(myShiroCasRealm);
    // <!-- 用户授权/认证信息Cache, 采用EhCache 缓存 --> 
            dwsm.setCacheManager(getEhCacheManager());
            // 指定 SubjectFactory
            dwsm.setSubjectFactory(new CasSubjectFactory());
            return dwsm;
        }
    
        @Bean
        public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
            aasa.setSecurityManager(securityManager);
            return aasa;
        }
    
        /** * 加载shiroFilter权限控制规则(从数据库读取然后配置) * * @author SHANHY * @create 2016年1月14日 */
        private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean, StudentService stuService, IScoreDao scoreDao){
            /////////////////////// 下面这些规则配置最好配置到配置文件中 ///////////////////////
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
    
            filterChainDefinitionMap.put(casFilterUrlPattern, "casFilter");// shiro集成cas后,首先添加该规则
    
            // authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
            filterChainDefinitionMap.put("/user", "authc");// 这里为了测试,只限制/user,实际开发中请修改为具体拦截的请求规则
            // anon:它对应的过滤器里面是空的,什么都没做
            logger.info("##################从数据库读取权限规则,加载到shiroFilter中##################");
            filterChainDefinitionMap.put("/user/edit/**", "authc,perms[user:edit]");// 这里为了测试,固定写死的值,也可以从数据库或其他配置中读取
    
            filterChainDefinitionMap.put("/login", "anon");
            filterChainDefinitionMap.put("/**", "anon");//anon 可以理解为不拦截
    
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        }
    
        /** * CAS过滤器 * * @return * @author SHANHY * @create 2016年1月17日 */
        @Bean(name = "casFilter")
        public CasFilter getCasFilter() {
            CasFilter casFilter = new CasFilter();
            casFilter.setName("casFilter");
            casFilter.setEnabled(true);
            // 登录失败后跳转的URL,也就是 Shiro 执行 CasRealm 的 doGetAuthenticationInfo 方法向CasServer验证tiket
            casFilter.setFailureUrl(loginUrl);// 我们选择认证失败后再打开登录页面
            return casFilter;
        }
    
        /** * ShiroFilter<br/> * 注意这里参数中的 StudentService 和 IScoreDao 只是一个例子,因为我们在这里可以用这样的方式获取到相关访问数据库的对象, * 然后读取数据库相关配置,配置到 shiroFilterFactoryBean 的访问规则中。实际项目中,请使用自己的Service来处理业务逻辑。 * * @param myShiroCasRealm * @param stuService * @param scoreDao * @return * @author SHANHY * @create 2016年1月14日 */
        @Bean(name = "shiroFilter")
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager, CasFilter casFilter, StudentService stuService, IScoreDao scoreDao) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            // 必须设置 SecurityManager 
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
            shiroFilterFactoryBean.setLoginUrl(loginUrl);
            // 登录成功后要跳转的连接
            shiroFilterFactoryBean.setSuccessUrl("/user");
            shiroFilterFactoryBean.setUnauthorizedUrl("/403");
            // 添加casFilter到shiroFilter中
            Map<String, Filter> filters = new HashMap<>();
            filters.put("casFilter", casFilter);
            shiroFilterFactoryBean.setFilters(filters);
    
            loadShiroFilterChain(shiroFilterFactoryBean, stuService, scoreDao);
            return shiroFilterFactoryBean;
        }
    
    }

    创建权限认证的 MyShiroCasRealm.java

    package org.springboot.sample.security;
    
    import java.util.List;
    
    import javax.annotation.PostConstruct;
    
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.cas.CasRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springboot.sample.config.ShiroCasConfiguration;
    import org.springboot.sample.dao.IUserDao;
    import org.springboot.sample.entity.Role;
    import org.springboot.sample.entity.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    @Service("myShiroCasRealm")
    @Transactional
    public class MyShiroCasRealm extends CasRealm{
    
        private static final Logger logger = LoggerFactory.getLogger(MyShiroCasRealm.class);
    
        @Autowired
        private IUserDao userDao; 
    
        @PostConstruct
        public void initProperty(){
    // setDefaultRoles("ROLE_USER");
            setCasServerUrlPrefix(ShiroCasConfiguration.casServerUrlPrefix);
            // 客户端回调地址
            setCasService(ShiroCasConfiguration.shiroServerUrlPrefix + ShiroCasConfiguration.casFilterUrlPattern);
        }
    
        /** * 权限认证,为当前登录的Subject授予角色和权限 * @see 经测试:本例中该方法的调用时机为需授权资源被访问时 * @see 经测试:并且每次访问需授权资源时都会执行该方法中的逻辑,这表明本例中默认并未启用AuthorizationCache * @see 经测试:如果连续访问同一个URL(比如刷新),该方法不会被重复调用,Shiro有一个时间间隔(也就是cache时间,在ehcache-shiro.xml中配置),超过这个时间间隔再刷新页面,该方法会被执行 */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            logger.info("##################执行Shiro权限认证##################");
            //获取当前登录输入的用户名,等价于(String) principalCollection.fromRealm(getName()).iterator().next();
            String loginName = (String)super.getAvailablePrincipal(principalCollection); 
            //到数据库查是否有此对象
            User user=userDao.findByName(loginName);// 实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
            if(user!=null){
                //权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission)
                SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
                //用户的角色集合
                info.setRoles(user.getRolesName());
                //用户的角色对应的所有权限,如果只使用角色定义访问权限,下面的四行可以不要
                List<Role> roleList=user.getRoleList();
                for (Role role : roleList) {
                    info.addStringPermissions(role.getPermissionsName());
                }
                // 或者按下面这样添加
                //添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色 
    // simpleAuthorInfo.addRole("admin"); 
                //添加权限 
    // simpleAuthorInfo.addStringPermission("admin:manage"); 
    // logger.info("已为用户[mike]赋予了[admin]角色和[admin:manage]权限");
                return info;
            }
            // 返回null的话,就会导致任何用户访问被拦截的请求时,都会自动跳转到unauthorizedUrl指定的地址
            return null;
        }
    
    }

    在Controller中添加一个方法,用于将登录URL简单化,提供一个重定向功能

      @RequestMapping(value="/login",method=RequestMethod.GET)
        public String loginForm(Model model){
            model.addAttribute("user", new User());
    // return "login";
            return "redirect:" + ShiroCasConfiguration.loginUrl;
        }

    本文主要是介绍如何在Spring Boot中集成Shiro+Cas,并非一个从零创建工程到整体完成的介绍。 
    上面贴出了2个主要的Java类,整个工程的其他所有代码没有任何与Shiro和Cas耦合的地方。如果需要jsp页面、Controller、实体类、连接数据库测试数据等代码,可以先参考文章:http://blog.csdn.net/catoop/article/details/50520958(建议先看这篇文章再看本文)

  • 相关阅读:
    可爱的中国电信 请问我们的电脑还属于我们自己吗?
    了解客户的需求,写出的代码或许才是最优秀的............
    DELPHI DATASNAP 入门操作(3)简单的主从表的简单更新【含简单事务处理】
    用数组公式获取字符在字符串中最后出现的位置
    在ehlib的DBGridEh控件中使用过滤功能(可以不用 MemTableEh 控件 适用ehlib 5.2 ehlib 5.3)
    格式化json返回的时间
    ExtJs中使用Ajax赋值给全局变量异常解决方案
    java compiler level does not match the version of the installed java project facet (转)
    收集的资料(六)ASP.NET编程中的十大技巧
    收集的资料共享出来(五)Asp.Net 权限解决办法
  • 原文地址:https://www.cnblogs.com/a8457013/p/7875866.html
Copyright © 2011-2022 走看看