Shiro+Cas微服务化及前后端完全分离

本文实例为大家分享了Shiro Cas微服务化及前后端完全分离,供大家参考,具体内容如下

shiro+cas微服务化笔记

1.Spring Boot 配置

有如下两个配置文件:ShiroBaseConfig.java

import lombok.extern.log4j.Log4j;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.cas.CasFilter;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * <p>
 * Description: shiro权限管理模块conf
 *
 * @author Dean.Hwang
 * @date 17/5/18
 */
@Configuration
@Log4j
public class ShiroBaseConfiguration {
  @Value("${cas.server.url.prefix}")
  private String casPrefix;
  @Value("${cas.service}")
  private String casService;

  /**
   * 会话Cookie模板
   *
   * @return
   */
  @Bean
  public SimpleCookie sessionIdCookie() {
    SimpleCookie simpleCookie = new SimpleCookie("sid");
    simpleCookie.setHttpOnly(true);
    simpleCookie.setMaxAge(1800000);
    return simpleCookie;
  }

  /**
   * 会话Cookie模板
   *
   * @return
   */
  @Bean
  public SimpleCookie rememberCookie() {
    SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
    simpleCookie.setHttpOnly(true);
    simpleCookie.setMaxAge(2592000);//30天
    return simpleCookie;
  }

  /**
   * rememberMe 管理器
   *
   * @return
   */
  @Bean
  public CookieRememberMeManager rememberMeManager(SimpleCookie rememberCookie) {
    CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
    cookieRememberMeManager.setCipherKey(Base64.decode(""));// rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
    cookieRememberMeManager.setCookie(rememberCookie);
    return cookieRememberMeManager;
  }

  /**
   * 会话DAO
   *
   * @return
   */
  @Bean
  public MemorySessionDAO sessionDAO() {
    return new MemorySessionDAO();
  }

  @Bean
  public CacheManager shiroCacheManager() {
    return new MemoryConstrainedCacheManager();
  }

  @Bean
  public KryCasRealm casRealm(CacheManager shiroCacheManager) {
    return new KryCasRealm(casPrefix, casService, shiroCacheManager);
  }

  @Bean
  public CasFilter casFilter() {
    CasFilter casFilter = new CasFilter();
    casFilter.setEnabled(true);
    casFilter.setName("casFilter");
    casFilter.setFailureUrl("/authority/casFailure");
    return casFilter;
  }

}

下面ShiroManagerConfiguration.java 文件

import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cas.CasFilter;
import org.apache.shiro.cas.CasSubjectFactory;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**
 * </p>
 * <p>
 * Copyright: Copyright (c) 2015
 * </p>
 * <p>
 * </p>
 *
 * @author Dean.Hwang
 * @date 17/5/18
 */
@Configuration
@AutoConfigureAfter(
    {ShiroBaseConfiguration.class}
)
public class ShiroManagerConfiguration {
  @Autowired
  private KryCasRealm kryCasRealm;
  @Autowired
  private CacheManager shiroCacheManager;
  @Autowired
  private CookieRememberMeManager rememberMeManager;
  @Value("${cas.server.login.url}")
  private String loginUrl;
  @Value("${cas.client.url.prefix}")
  private String urlPrefix;
  @Autowired
  private CasFilter casFilter;
  @Value("${cas.server.logout.url}")
  private String logoutUrl;
  @Value("${cas.client.index.url}")
  private String indexUrl;

  @Bean
  public DefaultWebSecurityManager securityManager() {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(kryCasRealm);
    securityManager.setSessionManager(new ServletContainerSessionManager());
    securityManager.setCacheManager(shiroCacheManager);
    securityManager.setRememberMeManager(rememberMeManager);
    securityManager.setSubjectFactory(new CasSubjectFactory());
    return securityManager;
  }

  /**
   * 相当于调用SecurityUtils.setSecurityManager(securityManager)
   *
   * @param securityManager
   * @return
   */
  @Bean
  public MethodInvokingFactoryBean methodInvokingFactoryBean(DefaultWebSecurityManager securityManager) {
    MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean();
    bean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
    bean.setArguments(new Object[]{securityManager});
    return bean;
  }

  @Bean
  public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
     ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
    factoryBean.setSecurityManager(securityManager);
    factoryBean.setLoginUrl(loginUrl + serviceStr + urlPrefix + "/cas");
    factoryBean.setSuccessUrl("../mind/index.do");
    factoryBean.setUnauthorizedUrl("/unauthorized.jsp");
    Map<String, Filter> filterMap = new HashMap<>();
    filterMap.put("cas", casFilter);
    filterMap.put("user", portalUserFilter);
    //只能在这里初始化LogoutFilter,不然会被spring boot注册到/*
    PortalLogoutFilter logoutFilter = new PortalLogoutFilter();
    logoutFilter.setRedirectUrl(logoutUrl + serviceStr + indexUrl);
    filterMap.put("logout", logoutFilter);
    factoryBean.setFilters(filterMap);
    Map<String, String> filters = new HashMap<>();
    filters.put("/casFailure.jsp", "anon");
    filters.put("/js/**", "anon");
    filters.put("/themes/**", "anon");
    filters.put("/3rdOauth/**", "anon");
    filters.put("/cas", "cas");
    filters.put("/logout", "logout");
    filters.put("/**", "user");
    factoryBean.setFilterChainDefinitionMap(filters);
    return factoryBean;  }
}  

2.UserFilter的改造

2.1改造的原因:

因为,我们现在新的服务器架构是前后端完全分离的。但是,shiro是不支持完全的前后端分离。所以导致了单点登录完成以后会跳转至接口,而不是目标页面。同时,由于历史原因,我们的cas验证服务器与业务服务器不是同一个域。如果,需要在服务器端进行重定向就必须要通过跨域,考虑到跨域会有风险。所以,我也将sso服务器登录重定向进行了重构。做成了返回json,前端在接收到json自己进行登录页跳转。

具体的实现代码如下:

protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
    Session session = SecurityUtils.getSubject().getSession();
    if (session != null) {
      SavedRequest savedRequest = new PortalSavedRequest(WebUtils.toHttp(request));//重写的SavedRequest,具体处理由不同的业务需求自定
      session.setAttribute(SAVED_REQUEST_KEY, savedRequest);

    }
    PrintWriter out = null;
    try {
      ResultVO<Object> vo = ResultVO.isRedirect();
      RedirectInfo info = new RedirectInfo(loginRedirectUrl);
      vo.setData(info);
      response.setCharacterEncoding("UTF-8");
      response.setContentType("application/json; charset=utf-8");
      out = response.getWriter();
      out.write(JsonMapper.nonDefaultMapper().toJson(vo));
    } catch (IOException e) {
      log.error("登录重定向失败(Login Redirect Failed)", e);
    } finally {
      if (out != null) {
        out.close();
      }
    }
  }

此方法是将Cas中的Userfilter进行了重写,并且在配置时使用重写的类对原有的UserFilter进行了覆盖。

#3.登录成功后的重定向:

由于在sso验证服务器登录成功以后会重定向到本地业务服务器上。本地业务服务器验证登录成功以后会默认重定向至配置的SuccessUrl。这样并不能将页面跳转回用户的原来请求的页面。所以我重写了CasFilter中的issueSuccessRedirect达到这个目的

/**
 * <p>
 * Copyright: Copyright (c) 2015
 * </p>
 *
 * @author Dean.Hwang
 * @date 17/7/17
 */
public class PortalCasFilter extends CasFilter {

  @Override
  protected void issueSuccessRedirect(ServletRequest request, ServletResponse response) throws Exception {
    String successUrl = ((ShiroHttpServletRequest) request).getHeader("page-url");//前端页面在请求的时候在header中带上请求这个接口的url。这样便将登录成功后需要跳转的地址绑定到了对应的Subject对象中。以便于在登录以后跳转到这个页面
    if (StringUtil.isBlank(successUrl)) {
      WebUtils.redirectToSavedRequest(request, response, getSuccessUrl());
    } else {
      WebUtils.redirectToSavedRequest(request, response, successUrl);
    }
  }
}

#4.用户安全的退出

后期发现直接依靠原有的logout会发生session未注销的情况。所以重写了LogoutFilter。登出的时候直接调用配置的URL即可

/**

 * </p>
 * <p>
 * Copyright: Copyright (c) 2015
 * </p>
 *
 * @author Dean.Hwang
 * @date 17/7/17
 */
public class PortalLogoutFilter extends AdviceFilter {

  private static final Logger log = LoggerFactory.getLogger(LogoutFilter.class);

  public static final String DEFAULT_REDIRECT_URL = "/";

  private String redirectUrl = DEFAULT_REDIRECT_URL;

  @Override
  protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
    Subject subject = getSubject(request, response);
    String redirectUrl = getRedirectUrl(request, response, subject);
    //try/catch added for SHIRO-298:
    try {
      subject.logout();
      Session session = subject.getSession();
      session.stop();
    } catch (SessionException ise) {
      log.debug("Encountered session exception during logout. This can generally safely be ignored.", ise);
    }
    issueRedirect(request, response, redirectUrl);
    return false;
  }
}

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

(0)

相关推荐

  • shiro整合springboot前后端分离

    本文实例为大家分享了shiro整合springboot前后端分离的具体代码,供大家参考,具体内容如下 1.shiro整合springboot的配置 package com.hisi.config; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.Filter; import org.apache.shiro.session.mgt.eis.MemorySessionDAO; import org

  • SpringBoot + Shiro前后端分离权限

    shiro 验证通过后的信息保存在session 中,而ajax 每次传的都是不同的sessionid ,所以主要的区别就是需要修改shiro获取sessionid的方式.这里使用的是登录后将后台的sessionid 传到前端然后存放到 cookie(这个存放的地方视情况而定),然后每次请求后端时在Header中携带此信息,这里起名为Authorization shiro 中 默认获取Sessionid的类是 DefaultWebSessionManager 所以需要重写此类 import or

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

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

  • Shiro+Cas微服务化及前后端完全分离

    本文实例为大家分享了Shiro Cas微服务化及前后端完全分离,供大家参考,具体内容如下 shiro+cas微服务化笔记 1.Spring Boot 配置 有如下两个配置文件:ShiroBaseConfig.java import lombok.extern.log4j.Log4j; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.cache.MemoryConstrainedCacheManager; im

  • Vue实现前后端完全分离的项目实战

    目录 1.Vue 2. 安装Nodejs服务器 2.1安装npm 2.2 安装vue的脚手架 3. 使用脚手架创建Vue工程 3.1.在cmd窗口中输入命令,打开创建界面 3.2创建 3.3创建完成后 4.使用WebStorm打开Vue工程 5.App.vue组件 5.1一个组件引用另一个组件: 5.2父组件如何向子组件传值 6.浅谈路由 1.Vue 我们之前项目中也使用到了Vue,但是我们之前的vue和我们的java代码是不是都在一个工程下.没有做到前后端完成分离.企业中前端也会由自己的服务器

  • spring boot+vue 的前后端分离与合并方案实例详解

    springboot和vue结合的方案网络上的主要有以下两种: 1. [不推荐]在html中直接使用script标签引入vue和一些常用的组件,这种方式和以前传统的开发是一样的,只是可以很爽的使用vue的双向数据绑定,这种方式只适合于普通的全栈开发. 2.[推荐]使用vue官方的脚手架创建单独的前端工程项目,做到和后端完全独立开发和部署,后端单独部署一个纯restful的服务,而前端直接采用nginx来部署,这种称为完全的前后端分离架构开发模式,但是在分离中有很多api权限的问题需要解决,包括部

  • SpringBoot+Vue.js实现前后端分离的文件上传功能

    这篇文章需要一定Vue和SpringBoot的知识,分为两个项目,一个是前端Vue项目,一个是后端SpringBoot项目. 后端项目搭建 我使用的是SpringBoot1.5.10+JDK8+IDEA 使用IDEA新建一个SpringBoot项目,一直点next即可 项目创建成功后,maven的pom配置如下 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> &l

  • 详解Vue微信授权登录前后端分离较为优雅的解决方案

    微信授权登录是一个非常常见的场景,利用微信授权登录,我们可以很容易获取用户的一些信息,通过用户对公众号的唯一openid从而建立数据库绑定用户身份. 微信授权登录的机制这里不做详述,微信官方文档已有详述,简述就是通过跳转微信授权的页面,用户点击确认后,微信会跳到回调页面,此时回调页面url上会携带code参数,通过code参数,后端可以拿code换取拥护openid,或者用户信息 在vue项目中,通常是一个SPA应用,即所有的页面都是同一个html,通常现在开发也是前后端彻底分离的,vue打包后

  • springBoot前后端分离项目中shiro的302跳转问题

    springBoot前后端分离项目shiro的302跳转 项目是使用的springboot ,使用的shiro做的用户鉴权.在前端请求时当用户信息失效,session失效的时候,shiro会重定向到配置的login.jsp 页面,或者是自己配置的logUrl. 因是前后端分离项目,与静态资源文件分离,固重定向后,接着会404. 经过查找网上配置资料,发现302原因是 FormAuthenticationFilter中onAccessDenied 方法做了相应处理.那知道问题所在,就可以有解决方了

  • Springboot+Vue+shiro实现前后端分离、权限控制的示例代码

    本文总结自实习中对项目的重构.原先项目采用Springboot+freemarker模版,开发过程中觉得前端逻辑写的实在恶心,后端Controller层还必须返回Freemarker模版的ModelAndView,逐渐有了前后端分离的想法,由于之前,没有接触过,主要参考的还是网上的一些博客教程等,初步完成了前后端分离,在此记录以备查阅. 一.前后端分离思想 前端从后端剥离,形成一个前端工程,前端只利用Json来和后端进行交互,后端不返回页面,只返回Json数据.前后端之间完全通过public A

  • Spring Boot + Vue 前后端分离项目如何踢掉已登录用户

    上篇文章中,我们讲了在 Spring Security 中如何踢掉前一个登录用户,或者禁止用户二次登录,通过一个简单的案例,实现了我们想要的效果. 但是有一个不太完美的地方,就是我们的用户是配置在内存中的用户,我们没有将用户放到数据库中去.正常情况下,松哥在 Spring Security 系列中讲的其他配置,大家只需要参考Spring Security+Spring Data Jpa 强强联手,安全管理只有更简单!一文,将数据切换为数据库中的数据即可. 本文是本系列的第十三篇,阅读前面文章有助

随机推荐