详解Spring Boot 集成Shiro和CAS

发布时间 - 2026-01-11 01:09:15    点击率:

请大家在看本文之前,先了解如下知识点:

1、Shiro 是什么?怎么用?

2、Cas 是什么?怎么用?

3、最好有spring基础

首先看一下下面这张图:

第一个流程是单纯使用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)
 * @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;

  @Bean
  public EhCacheManager getEhCacheManager() { 
    EhCacheManager em = new EhCacheManager(); 
    em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml"); 
    return em; 
  } 

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

  /**
   * 注册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;

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,并非一个从零创建工程到整体完成的介绍。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


# springboot  # shiro  # cas  # spring集成  # springboot集成shiro  # spring boot 1.5.4 集成shiro+cas  # 实现单点登录和权限控制  # SpringBoot集成Shiro进行权限控制和管理的示例  # springmvc集成shiro登录权限示例代码  # spring boot集成shiro详细教程(小结)  # spring boot 集成shiro的配置方法  # Spring Boot集成Shiro并利用MongoDB做Session存储的方法详解  # 详解spring与shiro集成  # shiro无状态web集成的示例代码  # Spring 应用中集成 Apache Shiro的方法  # Shiro集成Spring之注解示例详解  # 按下  # 跳转  # 自己的  # 本例  # 加载  # 看着  # 就会  # 如果你  # 在这里  # 第一个  # 可以用  # 请大家  # 你想  # 它是  # 所需  # 请使用  # 第二个  # 便可  # 或其他  # 不做 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: php8.4header发送头信息失败怎么办_php8.4header函数问题解决【解答】  html如何与html链接_实现多个HTML页面互相链接【互相】  标题:Vue + Vuex + JWT 身份认证的正确实践与常见误区解析  如何在IIS服务器上快速部署高效网站?  如何快速登录WAP自助建站平台?  JavaScript中如何操作剪贴板_ClipboardAPI怎么用  Win11怎么关闭资讯和兴趣_Windows11任务栏设置隐藏小组件  潮流网站制作头像软件下载,适合母子的网名有哪些?  如何实现javascript表单验证_正则表达式有哪些实用技巧  Laravel如何使用Laravel Vite编译前端_Laravel10以上版本前端静态资源管理【教程】  Laravel如何集成微信支付SDK_Laravel使用yansongda-pay实现扫码支付【实战】  成都网站制作公司哪家好,四川省职工服务网是做什么用?  Firefox Developer Edition开发者版本入口  音乐网站服务器如何优化API响应速度?  如何快速辨别茅台真假?关键步骤解析  Edge浏览器如何截图和滚动截图_微软Edge网页捕获功能使用教程【技巧】  Laravel模型事件有哪些_Laravel Model Event生命周期详解  如何制作一个表白网站视频,关于勇敢表白的小标题?  Win11任务栏卡死怎么办 Windows11任务栏无反应解决方法【教程】  javascript中的数组方法有哪些_如何利用数组方法简化数据处理  详解免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)  电商网站制作价格怎么算,网上拍卖流程以及规则?  南京网站制作费用,南京远驱官方网站?  Laravel策略(Policy)如何控制权限_Laravel Gates与Policies实现用户授权  python中快速进行多个字符替换的方法小结  Edge浏览器怎么启用睡眠标签页_节省电脑内存占用优化技巧  最好的网站制作公司,网购哪个网站口碑最好,推荐几个?谢谢?  网站建设保证美观性,需要考虑的几点问题!  Laravel怎么实现软删除SoftDeletes_Laravel模型回收站功能与数据恢复【步骤】  Laravel如何配置和使用缓存?(Redis代码示例)  Laravel如何配置中间件Middleware_Laravel自定义中间件拦截请求与权限校验【步骤】  详解jQuery中基本的动画方法  laravel怎么配置和使用PHP-FPM来优化性能_laravel PHP-FPM配置与性能优化方法  JavaScript如何实现类型判断_typeof和instanceof有什么区别  如何快速使用云服务器搭建个人网站?  教学论文网站制作软件有哪些,写论文用什么软件 ?  PythonWeb开发入门教程_Flask快速构建Web应用  Laravel如何实现一对一模型关联?(Eloquent示例)  laravel怎么用DB facade执行原生SQL查询_laravel DB facade原生SQL执行方法  Laravel中间件如何使用_Laravel自定义中间件实现权限控制  如何打造高效商业网站?建站目的决定转化率  Swift中switch语句区间和元组模式匹配  利用vue写todolist单页应用  android nfc常用标签读取总结  UC浏览器如何切换小说阅读源_UC浏览器阅读源切换【方法】  JavaScript如何实现倒计时_时间函数如何精确控制  Laravel Debugbar怎么安装_Laravel调试工具栏配置指南  Java类加载基本过程详细介绍  成都品牌网站制作公司,成都营业执照年报网上怎么办理?  Laravel Vite是做什么的_Laravel前端资源打包工具Vite配置与使用