spring boot整合Shiro实现单点登录的示例代码

Shiro是什么

Shiro是一个Java平台的开源权限框架,用于认证和访问授权。具体来说,满足对如下元素的支持:

  1. 用户,角色,权限(仅仅是操作权限,数据权限必须与业务需求紧密结合),资源(url)。
  2. 用户分配角色,角色定义权限。
  3. 访问授权时支持角色或者权限,并且支持多级的权限定义。

Q:对组的支持?
A:shiro默认不支持对组设置权限。

Q:是否可以满足对组进行角色分配的需求?
A:扩展Realm,可以支持对组进行分配角色,其实就是给该组下的所有用户分配权限。

Q:对数据权限的支持? 在业务系统中定义?
A:shiro仅仅实现对操作权限的控制,用于在前端控制元素隐藏或者显示,以及对资源访问权限进行检查。数据权限与具体的业务需求紧密关联,shiro本身无法实现对数据权限的控制。

Q:动态权限分配?
A:扩展org.apache.shiro.realm.Realm,支持动态权限分配。

Q:与Spring集成?
A:可以支持与Spring集成,shiro还支持jsp标签。

前面的博客中,我们说道了Shiro的两个最大的特点,认证和授权,而单点登录也是属于认证的一部分,默认情况下,Shiro已经为我们实现了和Cas的集成,我们加入集成的一些配置就ok了。

1、加入shiro-cas包

<!-- shiro整合cas单点 -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-cas</artifactId>
      <version>1.2.4</version>
    </dependency>

2、加入单点登录的配置

这里,我将所有的配置都贴出来,方便参考,配置里面已经加了详尽的说明。

package com.chhliu.springboot.shiro.config;
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.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
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.context.annotation.DependsOn;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.DelegatingFilterProxy; 

/**
 * Shiro 配置
 *
 * Apache Shiro 核心通过 Filter 来实现,就好像SpringMvc 通过DispachServlet 来主控制一样。 既然是使用
 * Filter 一般也就能猜到,是通过URL规则来进行过滤和权限校验,所以我们需要定义一系列关于URL的规则和访问权限。
 *
 * @author chhliu
 */
@Configuration
public class ShiroConfiguration { 

  // cas server地址
  public static final String casServerUrlPrefix = "http://127.0.0.1";
  // Cas登录页面地址
  public static final String casLoginUrl = casServerUrlPrefix + "/login";
  // Cas登出页面地址
  public static final String casLogoutUrl = casServerUrlPrefix + "/logout";
  // 当前工程对外提供的服务地址
  public static final String shiroServerUrlPrefix = "http://127.0.1.28:8080";
  // casFilter UrlPattern
  public static final String casFilterUrlPattern = "/index";
  // 登录地址
  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="+loginUrl;
  // 登录成功地址
//  public static final String loginSuccessUrl = "/index";
  // 权限认证失败跳转地址
  public static final String unauthorizedUrl = "/error/403.html"; 

  /**
   * 实例化SecurityManager,该类是shiro的核心类
   * @return
   */
  @Bean
  public DefaultWebSecurityManager securityManager() {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(myShiroCasRealm());
//   <!-- 用户授权/认证信息Cache, 采用EhCache 缓存 -->
    securityManager.setCacheManager(getEhCacheManager());
    // 指定 SubjectFactory,如果要实现cas的remember me的功能,需要用到下面这个CasSubjectFactory,并设置到securityManager的subjectFactory中
    securityManager.setSubjectFactory(new CasSubjectFactory());
    return securityManager;
  } 

  /**
   * 配置缓存
   * @return
   */
  @Bean
  public EhCacheManager getEhCacheManager() {
    EhCacheManager em = new EhCacheManager();
    em.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
    return em;
  } 

  /**
   * 配置Realm,由于我们使用的是CasRealm,所以已经集成了单点登录的功能
   * @param cacheManager
   * @return
   */
  @Bean
  public MyShiroRealm myShiroCasRealm() {
    MyShiroRealm realm = new MyShiroRealm();
    // cas登录服务器地址前缀
    realm.setCasServerUrlPrefix(ShiroConfiguration.casServerUrlPrefix);
    // 客户端回调地址,登录成功后的跳转地址(自己的服务地址)
    realm.setCasService(ShiroConfiguration.shiroServerUrlPrefix + ShiroConfiguration.casFilterUrlPattern);
    // 登录成功后的默认角色,此处默认为user角色
    realm.setDefaultRoles("user");
    return realm;
  } 

  /**
   * 注册单点登出的listener
   * @return
   */
  @SuppressWarnings({ "rawtypes", "unchecked" })
  @Bean
  @Order(Ordered.HIGHEST_PRECEDENCE)// 优先级需要高于Cas的Filter
  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;
  } 

  /**
   * 该类可以保证实现了org.apache.shiro.util.Initializable接口的shiro对象的init或者是destory方法被自动调用,
   * 而不用手动指定init-method或者是destory-method方法
   * 注意:如果使用了该类,则不需要手动指定初始化方法和销毁方法,否则会出错
   * @return
   */
  @Bean(name = "lifecycleBeanPostProcessor")
  public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
    return new LifecycleBeanPostProcessor();
  } 

  /**
   * 下面两个配置主要用来开启shiro aop注解支持. 使用代理方式;所以需要开启代码支持;
   * @return
   */
  @Bean
  @DependsOn("lifecycleBeanPostProcessor")
  public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
    DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
    daap.setProxyTargetClass(true);
    return daap;
  } 

  /**
   * @param securityManager
   * @return
   */
  @Bean
  public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    return authorizationAttributeSourceAdvisor;
  } 

  /**
   * 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);// 我们选择认证失败后再打开登录页面
    casFilter.setLoginUrl(loginUrl);
    return casFilter;
  } 

  /**
   * 使用工厂模式,创建并初始化ShiroFilter
   * @param securityManager
   * @param casFilter
   * @return
   */
  @Bean(name = "shiroFilter")
  public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager, CasFilter casFilter) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    // 必须设置 SecurityManager
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
    shiroFilterFactoryBean.setLoginUrl(loginUrl);
    /*
     * 登录成功后要跳转的连接,不设置的时候,会默认跳转到前一步的url
     * 比如先在浏览器中输入了http://localhost:8080/userlist,但是现在用户却没有登录,于是会跳转到登录页面,等登录认证通过后,
     * 页面会再次自动跳转到http://localhost:8080/userlist页面而不是登录成功后的index页面
     * 建议不要设置这个字段
     */
//    shiroFilterFactoryBean.setSuccessUrl(loginSuccessUrl); 

    // 设置无权限访问页面
    shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
    /*
     * 添加casFilter到shiroFilter中,注意,casFilter需要放到shiroFilter的前面,
     * 从而保证程序在进入shiro的login登录之前就会进入单点认证
     */
    Map<String, Filter> filters = new LinkedHashMap<>();
    filters.put("casFilter", casFilter); 

    // logout已经被单点登录的logout取代
    // filters.put("logout",logoutFilter());
    shiroFilterFactoryBean.setFilters(filters); 

    loadShiroFilterChain(shiroFilterFactoryBean);
    return shiroFilterFactoryBean;
  } 

  /**
   * 加载shiroFilter权限控制规则(从数据库读取然后配置),角色/权限信息由MyShiroCasRealm对象提供doGetAuthorizationInfo实现获取来的
   * 生产中会将这部分规则放到数据库中
   * @param shiroFilterFactoryBean
   */
  private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean){
    /////////////////////// 下面这些规则配置最好配置到配置文件中,注意,此处加入的filter需要保证有序,所以用的LinkedHashMap ///////////////////////
    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
    filterChainDefinitionMap.put(casFilterUrlPattern, "casFilter");
    //2.不拦截的请求
    filterChainDefinitionMap.put("/css/**","anon");
    filterChainDefinitionMap.put("/js/**","anon");
    filterChainDefinitionMap.put("/login", "anon");
    // 此处将logout页面设置为anon,而不是logout,因为logout被单点处理,而不需要再被shiro的logoutFilter进行拦截
    filterChainDefinitionMap.put("/logout","anon");
    filterChainDefinitionMap.put("/error","anon");
    //3.拦截的请求(从本地数据库获取或者从casserver获取(webservice,http等远程方式),看你的角色权限配置在哪里)
    filterChainDefinitionMap.put("/user", "authc"); //需要登录
    //4.登录过的不拦截
    filterChainDefinitionMap.put("/**", "authc");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
  }
}

部分配置参考:http://shiro.apache.org/spring.html

3、编写Realm

由于需要集成单点登录的功能,所以需要集成CasRealm类,该类已经为我们实现了单点认证的功能,我们要做的就是实现授权部分的功能,示例代码如下:

package com.chhliu.springboot.shiro.config;
import javax.annotation.Resource;
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 com.chhliu.springboot.shiro.mode.SysPermission;
import com.chhliu.springboot.shiro.mode.SysRole;
import com.chhliu.springboot.shiro.mode.UserInfo;
import com.chhliu.springboot.shiro.service.UserInfoService; 

/**
 * 权限校验核心类; 由于使用了单点登录,所以无需再进行身份认证 只需要授权即可
 *
 * @author chhliu
 */
public class MyShiroRealm extends CasRealm { 

  @Resource
  private UserInfoService userInfoService; 

  /**
   * 1、CAS认证 ,验证用户身份
   * 2、将用户基本信息设置到会话中,方便获取
   * 3、该方法可以直接使用CasRealm中的认证方法,此处仅用作测试
   */
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) { 

    // 调用父类中的认证方法,CasRealm已经为我们实现了单点认证。
    AuthenticationInfo authc = super.doGetAuthenticationInfo(token); 

    // 获取登录的账号,cas认证成功后,会将账号存起来
    String account = (String) authc.getPrincipals().getPrimaryPrincipal(); 

    // 将用户信息存入session中,方便程序获取,此处可以将根据登录账号查询出的用户信息放到session中
    SecurityUtils.getSubject().getSession().setAttribute("no", account); 

    return authc;
  } 

  /**
   * 此方法调用 hasRole,hasPermission的时候才会进行回调.
   *
   * 权限信息.(授权): 1、如果用户正常退出,缓存自动清空; 2、如果用户非正常退出,缓存自动清空;
   * 3、如果我们修改了用户的权限,而用户不退出系统,修改的权限无法立即生效。 (需要手动编程进行实现;放在service进行调用)
   * 在权限修改后调用realm中的方法,realm已经由spring管理,所以从spring中获取realm实例, 调用clearCached方法;
   * :Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
   *
   * @param principals
   * @return
   */
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()"); 

    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    // 获取单点登陆后的用户名,也可以从session中获取,因为在认证成功后,已经将用户名放到session中去了
    String userName = (String) super.getAvailablePrincipal(principals);
//       principals.getPrimaryPrincipal(); 这种方式也可以获取用户名 

    // 根据用户名获取该用户的角色和权限信息
    UserInfo userInfo = userInfoService.findByUsername(userName); 

    // 将用户对应的角色和权限信息打包放到AuthorizationInfo中
    for (SysRole role : userInfo.getRoleList()) {
      authorizationInfo.addRole(role.getRole());
      for (SysPermission p : role.getPermissions()) {
        authorizationInfo.addStringPermission(p.getPermission());
      }
    } 

    return authorizationInfo;
  }
}

下面,我们就可以进行验证测试了!

在浏览器输入http:127.0.1.28:8080/userInfo/userList 我们会发现,会自动跳转到单点的登录页面

然后我们输入用户名和密码,就会自动跳转到http:127.0.1.28:8080/userInfo/userList页面了。

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

您可能感兴趣的文章:

  • spring boot 1.5.4 集成shiro+cas,实现单点登录和权限控制
(0)

相关推荐

  • spring boot 1.5.4 集成shiro+cas,实现单点登录和权限控制

    1.添加maven依赖(先安装好cas-server-3.5.2,安装步骤请查看本文参考文章) <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>

  • spring boot整合Shiro实现单点登录的示例代码

    Shiro是什么 Shiro是一个Java平台的开源权限框架,用于认证和访问授权.具体来说,满足对如下元素的支持: 用户,角色,权限(仅仅是操作权限,数据权限必须与业务需求紧密结合),资源(url). 用户分配角色,角色定义权限. 访问授权时支持角色或者权限,并且支持多级的权限定义. Q:对组的支持? A:shiro默认不支持对组设置权限. Q:是否可以满足对组进行角色分配的需求? A:扩展Realm,可以支持对组进行分配角色,其实就是给该组下的所有用户分配权限. Q:对数据权限的支持? 在业务

  • SpringBoot整合Keycloak实现单点登录的示例代码

    目录 1. 搭建Keycloak服务器 2. 配置权限 2.1. 登陆 2.2. 创建Realm 2.3. 创建用户 2.4. 创建客户端 2.5. 创建角色 2.6. 配置用户角色关系 2.7. 配置客户端和角色关系 3. 整合SpringBoot 3.1. 引入核心依赖 3.2. 编写Controller 3.3. 编写application.yml 4. 验证 Keycloak是一个开源的身份和权限访问管理工具,轻松为应用程序和安全服务添加身份验证,无需处理储存用户或者验证用户,其提供用户

  • spring boot整合shiro安全框架过程解析

    这篇文章主要介绍了spring boot整合shiro安全框架过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 题记:在学习了springboot和thymeleaf之后,想完成一个项目练练手,于是使用springboot+mybatis和thymeleaf完成一个博客系统,在完成的过程中出现的一些问题,将这些问题记录下来,作为自己的学习心得.在这先感谢群主TyCoding的Tumo项目,虽然本人实在太菜了,好些地方看不懂,但还是使我受益

  • Spring boot整合shiro+jwt实现前后端分离

    本文实例为大家分享了Spring boot整合shiro+jwt实现前后端分离的具体代码,供大家参考,具体内容如下 这里内容很少很多都为贴的代码,具体内容我经过了看源码和帖子加了注释.帖子就没用太多的内容 先下载shiro和jwt的jar包 <!-- shiro包 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId

  • Spring Boot2开发之Spring Boot整合Shiro两种详细方法

    在 Spring Boot 中做权限管理,一般来说,主流的方案是 Spring Security ,但是,仅仅从技术角度来说,也可以使用 Shiro. Spring Security 和 Shiro 的比较: Spring Security 是一个重量级的安全管理框架:Shiro 则是一个轻量级的安全管理框架 Spring Security 概念复杂,配置繁琐:Shiro 概念简单.配置简单 Spring Security 功能强大:Shiro 功能简单 等等 虽然 Shiro 功能简单,但是也

  • Spring Boot 整合 Shiro+Thymeleaf过程解析

    这篇文章主要介绍了Spring Boot 整合 Shiro+Thymeleaf过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.导包 <!-- springboot 与 shiro 的集成--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <

  • Spring Boot 整合 TKMybatis 二次简化持久层代码的实现

    经常用 MyBatis 的的都知道,使用这个框架存在一个非常不友善的问题就是,就是每操作一个单表就需要自己手写一个 xml 文件,虽然说可以用工具生成 xml 和实体类可以解决这个问题,但是二次开发的时候对某个表字段进行修改的时候,生成 xml 文件就不现实啦.最近发现 tk.mybatis 就非常好的解决了这个问题.tk.mybatis 整合了 MyBatis 框架,在其基础上提供了很多工具,封装了常用的增删改查 SQL 语句,可以让我们的开发效率更高.在这里和大家分享一下. 引入依赖 在 p

  • SpringBoot中整合Shiro实现权限管理的示例代码

    之前在 SSM 项目中使用过 shiro,发现 shiro 的权限管理做的真不错,但是在 SSM 项目中的配置太繁杂了,于是这次在 SpringBoot 中使用了 shiro,下面一起看看吧 一.简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序. 三个核心组件: 1.Subject 即"当前操作用户".但是,在 Shi

  • springboot集成CAS实现单点登录的示例代码

    最近新参与的项目用到了cas单点登录,我还不会,这怎么能容忍!空了学习并搭建了一个spring-boot 集成CAS 的demo.实现了单点登录与登出. 单点登录英文全称是:Single Sign On,简称SSO. 含义:在多个相互信任的系统中,只要登录一个系统其他系统均可访问. CAS 是一种使用广泛的单点登录实现,分为客户端CAS Client和服务端 CAS Service,客户端就是我们的系统,服务端是认证中心,由CAS提供,我们需要稍作修改,启动起来就可以用.~~~~ 效果演示 ht

  • spring cloud oauth2 实现用户认证登录的示例代码

    需求 在微服务架构中,我们有很多业务模块,每个模块都需要有用户认证,权限校验.有时候也会接入来自第三方厂商的应用.要求是只登录一次,即可在各个服务的授权范围内进行操作.看到这个需求,立马就想到了这不就是单点登录吗?于是基于这样的需求,作者使用spring-cloud-oauth2去简单的实现了下用户认证和单点登录. 相关介绍 OAuth2 OAuth2是一个关于授权的网络标准,他定制了设计思路和执行流程.OAuth2一共有四种授权模式:授权码模式(authorization code).简化模式

随机推荐