欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解Spring Boot 集成Shiro和CAS

程序员文章站 2024-02-23 13:12:22
请大家在看本文之前,先了解如下知识点: 1、shiro 是什么?怎么用? 2、cas 是什么?怎么用? 3、最好有spring基础 首先看一下...

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

1、shiro 是什么?怎么用?

2、cas 是什么?怎么用?

3、最好有spring基础

首先看一下下面这张图:

第一个流程是单纯使用shiro的流程。

第二个流程是单纯使用cas的流程。

第三个图是shiro集成cas后的流程。

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

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