github:https://github.com/peterowang/shiro-cas    

本文如有配置问题,请查看之前的springboot集成shiro的文章

1.配置ehcache缓存,在resource下创建config,再创建ehcache-shiro.xml添加:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" name="shiroCache">

    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
    />
</ehcache>

2.添加maven依赖
<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-cas</artifactId>
            <version>1.2.4</version>
        </dependency>   
3.配置shiro+cas
添加



MyShiroCasRealm 类



package com.example.demo.config.shiro;

import com.example.demo.model.UserInfo;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
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.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;

/**
 * Created by BFD-593 on 2017/8/11.
 */
public class MyShiroCasRealm extends CasRealm {
    private static final Logger logger = LoggerFactory.getLogger(MyShiroCasRealm.class);
        @PostConstruct
        public void initProperty(){
            // cas地址
            setCasServerUrlPrefix(ShiroConfiguration.casServerUrlPrefix);
            // 客户端回调地址
            setCasService(ShiroConfiguration.shiroServerUrlPrefix + ShiroConfiguration.casFilterUrlPattern);
        }

//    /**
//     * 1、CAS认证 ,验证用户身份
//     * 2、将用户基本信息设置到会话中(不用了,随时可以获取的)
//     */
//    @Override
//    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
//
//        AuthenticationInfo authc = super.doGetAuthenticationInfo(token);
//
//        String account = (String) authc.getPrincipals().getPrimaryPrincipal();
//
//        User user = userDao.getByName(account);
//        //将用户信息存入session中
//        SecurityUtils.getSubject().getSession().setAttribute("user", user);
//
//        return authc;
//    }

    /**
     * 此方法调用 hasRole,hasPermission的时候才会进行回调.
     *
     * 权限信息.(授权): 1、如果用户正常退出,缓存自动清空; 2、如果用户非正常退出,缓存自动清空;
     * 3、如果我们修改了用户的权限,而用户不退出系统,修改的权限无法立即生效。 (需要手动编程进行实现;放在service进行调用)
     * 在权限修改后调用realm中的方法,realm已经由spring管理,所以从spring中获取realm实例, 调用clearCached方法;
     * :Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        logger.info("开始权限配置");
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
//        UserInfo userInfo = (UserInfo)principals.getPrimaryPrincipal();
        //这里应该查询数据库,拿到用户的所有角色,遍历添加角色到权限对象中,再通过角色获取权限,添加到权限对象中
       /* for (Role role: userInfo.getRoleList()) {
            authorizationInfo.addRole(role.getRole());
            for (SysPermission p: role.getPermissions()) {
                authorizationInfo.addStringPermission(p.getPermission());
            }
        }*/
        //为了节省时间,这边我先给它写死,做测试
        authorizationInfo.addRole("wangjing");
        authorizationInfo.addStringPermission("userinfo:view");
        return authorizationInfo;
    }
}




添加shiro配置类,主要是将casFilter添加到shiroFilter中
package com.example.demo.config.shiro;

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.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;

/**
 * shiro配置类
 * Created by BFD-593 on 2017/8/8.
 */
@Configuration
public class ShiroConfiguration {
    private static final Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);
    // cas server地址
    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:8089";
    // casFilter UrlPattern
    public static final String casFilterUrlPattern = "/cas";
    // 登录地址
    public static final String loginUrl = casLoginUrl + "?service=" + shiroServerUrlPrefix + casFilterUrlPattern;
    // 登出地址(casserver启用service跳转功能,需在webapps\cas\WEB-INF\cas.properties文件中启用cas.logout.followServiceRedirects=true)
    public static final String logoutUrl = casLogoutUrl+"?service="+shiroServerUrlPrefix;
    // 登录成功地址
    public static final String loginSuccessUrl = "/index";
    // 权限认证失败跳转地址
    public static final String unauthorizedUrl = "/403";
        @Bean
        public EhCacheManager getEhCacheManager() {
            EhCacheManager em = new EhCacheManager();
            em.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
            return em;
        }

        @Bean(name = "myShiroCasRealm")
        public MyShiroCasRealm myShiroCasRealm(EhCacheManager cacheManager) {
            MyShiroCasRealm realm = new MyShiroCasRealm();
            realm.setCacheManager(cacheManager);
            return realm;
        }

        /**
         * 注册单点登出listener
         * @return
         */
        @Bean
        public ServletListenerRegistrationBean singleSignOutHttpSessionListener(){
            ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean();
            bean.setListener(new SingleSignOutHttpSessionListener());
            bean.setEnabled(true);
            return bean;
        }

        /**
         * 注册单点登出filter
         * @return
         */
        @Bean
        public FilterRegistrationBean singleSignOutFilter(){
            FilterRegistrationBean bean = new FilterRegistrationBean();
            bean.setName("singleSignOutFilter");
            bean.setFilter(new SingleSignOutFilter());
            bean.addUrlPatterns("/*");
            bean.setEnabled(true);
            return bean;
        }



        /**
         * 注册DelegatingFilterProxy(Shiro)
         */
        @Bean
        public FilterRegistrationBean delegatingFilterProxy() {
            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(@Qualifier("myShiroCasRealm") MyShiroCasRealm myShiroCasRealm) {
            DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
            dwsm.setRealm(myShiroCasRealm);
            //用户授权/认证信息Cache, 采用EhCache 缓存
            dwsm.setCacheManager(getEhCacheManager());
            // 指定 SubjectFactory
            dwsm.setSubjectFactory(new CasSubjectFactory());
            return dwsm;
        }



        /**
         * CAS过滤器
         *
         * @return
         */
        @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
         * @param securityManager
         * @param casFilter
         * @return
         */
        @Bean(name = "shiroFilter")
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager,
                                                                @Qualifier("casFilter") CasFilter casFilter) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            // 必须设置 SecurityManager
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
            shiroFilterFactoryBean.setLoginUrl(loginUrl);
            // 登录成功后要跳转的连接
            shiroFilterFactoryBean.setSuccessUrl(loginSuccessUrl);
            shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
            // 添加casFilter到shiroFilter中
            Map<String, Filter> filters = new HashMap<>();
            filters.put("casFilter", casFilter);
            shiroFilterFactoryBean.setFilters(filters);

            loadShiroFilterChain(shiroFilterFactoryBean);
            return shiroFilterFactoryBean;
        }

        /**
         * 加载shiroFilter权限控制规则(从数据库读取然后配置),角色/权限信息由MyShiroCasRealm对象提供doGetAuthorizationInfo实现获取来的
         */
        private void loadShiroFilterChain(@Qualifier("shiroFilter") ShiroFilterFactoryBean shiroFilterFactoryBean){
            /// 下面这些规则配置最好配置到配置文件中 ///
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();

            // authc:该过滤器下的页面必须登录后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
            // anon: 可以理解为不拦截
            // user: 登录了就不拦截
            // roles["admin"] 用户拥有admin角色
            // perms["permission1"] 用户拥有permission1权限
            // filter顺序按照定义顺序匹配,匹配到就验证,验证完毕结束。
            // url匹配通配符支持:? * **,分别表示匹配1个,匹配0-n个(不含子路径),匹配下级所有路径

            //1.shiro集成cas后,首先添加该规则
            filterChainDefinitionMap.put(casFilterUrlPattern, "casFilter");

            //2.不拦截的请求
            filterChainDefinitionMap.put("/css/**","anon");
            filterChainDefinitionMap.put("/login", "anon");
            filterChainDefinitionMap.put("/logout","anon");
            filterChainDefinitionMap.put("/error","anon");

            //3.拦截的请求,并且拥有哪些权限才可以访问,当没有权限时
            //  我们通过在shiroFilter里设置 shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
            //  让其跳转到403页面,需要加403的controller请求哦
            //  这里还可以通过另一种方式,就是在controller的需要的请求上,加shiro的权限注解
            //  如果通过注解的方式,则需要通过以下两个配置bean来分别设置支持shiro注解和无权限跳转的页面
            filterChainDefinitionMap.put("/userinfo/userList", "authc,perms[\"userinfo:view\"],roles[\"wangjing\"]"); //需要登录,且用户有权限为userinfo:view并且角色为wangjing
            filterChainDefinitionMap.put("/userinfo/userDel", "authc,perms[\"userinfo:view\"],roles[\"admin\"]");
            filterChainDefinitionMap.put("/userinfo/userAdd", "authc,perms[\"userinfo:view\"],roles[\"redhat\"]");
            //4.登录过的不拦截
            filterChainDefinitionMap.put("/**", "user");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        }
/*
        *//**
         * 权限认证
         * 需要开启Shiro AOP注解支持
         * @RequiresPermissions({"userinfo:view"})
         * @RequiresRoles({"wangjing"})等注解的支持
         * @param securityManager
         * @return
         *//*
        @Bean
        public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
            aasa.setSecurityManager(securityManager);
            return aasa;
        }*/
        /**
         * 当用户无权限访问403页面而不抛异常,默认shiro会报UnauthorizedException异常
         * @return
         */
    /*    @Bean
        public SimpleMappingExceptionResolver resolver() {
            SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
            Properties properties = new Properties();
            properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/403");
            resolver.setExceptionMappings(properties);
            return resolver;
        }*/
}

添加HomeController
package com.example.demo.web;

import com.example.demo.config.shiro.ShiroConfiguration;
import com.example.demo.model.UserInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpSession;

/**
 * Created by BFD-593 on 2017/8/8.
 */
@Controller
public class HomeController {
    private static final Logger log = LoggerFactory.getLogger(HomeController.class);
    @RequestMapping({"/","/index"})
    public String index() {
        return "index";
    }

    /**
     * shiroFilterFactoryBean.setLoginUrl(loginUrl);我们设置了登录地址,但没设置logout,
     * 所以加一个logout的请求,转到cas的logout上
     * @param session
     * @return
     */
    @RequestMapping(value = "logout", method = { RequestMethod.GET,
            RequestMethod.POST })
    public String loginout(HttpSession session)
    {
        return "redirect:"+ShiroConfiguration.logoutUrl;
    }
    @RequestMapping("/403")
    public String fail(){
            return "403";
    }
}

添加UserController
package com.example.demo.web;

import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.authz.annotation.RequiresUser;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Created by BFD-593 on 2017/8/9.
 */
@Controller
@RequestMapping("/userinfo")
public class UserController {
    /**
     * 要查看必须有角色wangjing和有权限userinfo:view
     * @return
     */
    @RequestMapping("/userList")
    public String userInfo(){
        return "userInfo";
    }

    /**
     * 用户添加必须有查看和删除权限;
     * @return
     */
    @RequestMapping("/userAdd")
    public String userInfoAdd(){
        return "userAdd";
    }

    /**
     * 要删除必须有查看和删除权限
     * @return
     */
    @RequestMapping("/userDel")
    public String userInfoDel() {
        return "userDel";
    }

}

运行验证

登录 

访问:http://localhost:8089

跳转至:https://localhost:8443/cas/login?service=http://localhost:8089/cas

输入正确用户名密码登录跳转回:http://localhost:8089/cas?ticket=ST-203-GUheN64mOZec9IWZSH1B-cas01.example.org

最终跳回:http://localhost:8089/index

 

登出

访问:http://localhost:8089/logout

跳转至:https://localhost:8443/cas/logout?service=http://localhost:8089/cas

这次登录成功后返回:http://localhost:8089/index