溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Shiro+Cas微服務(wù)化及前后端完全分離

發(fā)布時(shí)間:2020-09-27 14:08:07 來源:腳本之家 閱讀:175 作者:云逸_java 欄目:編程語言

本文實(shí)例為大家分享了Shiro Cas微服務(wù)化及前后端完全分離,供大家參考,具體內(nèi)容如下

shiro+cas微服務(wù)化筆記

1.Spring Boot 配置

有如下兩個(gè)配置文件: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權(quán)限管理模塊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;
 
 /**
  * 會(huì)話Cookie模板
  *
  * @return
  */
 @Bean
 public SimpleCookie sessionIdCookie() {
  SimpleCookie simpleCookie = new SimpleCookie("sid");
  simpleCookie.setHttpOnly(true);
  simpleCookie.setMaxAge(1800000);
  return simpleCookie;
 }

 /**
  * 會(huì)話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加密的密鑰 建議每個(gè)項(xiàng)目都不一樣 默認(rèn)AES算法 密鑰長度(128 256 512 位)
  cookieRememberMeManager.setCookie(rememberCookie);
  return cookieRememberMeManager;
 }

 /**
  * 會(huì)話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;
 }

 /**
  * 相當(dāng)于調(diào)用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,不然會(huì)被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改造的原因:

因?yàn)?,我們現(xiàn)在新的服務(wù)器架構(gòu)是前后端完全分離的。但是,shiro是不支持完全的前后端分離。所以導(dǎo)致了單點(diǎn)登錄完成以后會(huì)跳轉(zhuǎn)至接口,而不是目標(biāo)頁面。同時(shí),由于歷史原因,我們的cas驗(yàn)證服務(wù)器與業(yè)務(wù)服務(wù)器不是同一個(gè)域。如果,需要在服務(wù)器端進(jìn)行重定向就必須要通過跨域,考慮到跨域會(huì)有風(fēng)險(xiǎn)。所以,我也將sso服務(wù)器登錄重定向進(jìn)行了重構(gòu)。做成了返回json,前端在接收到j(luò)son自己進(jìn)行登錄頁跳轉(zhuǎn)。

具體的實(shí)現(xiàn)代碼如下:

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,具體處理由不同的業(yè)務(wù)需求自定
   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進(jìn)行了重寫,并且在配置時(shí)使用重寫的類對原有的UserFilter進(jìn)行了覆蓋。

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

由于在sso驗(yàn)證服務(wù)器登錄成功以后會(huì)重定向到本地業(yè)務(wù)服務(wù)器上。本地業(yè)務(wù)服務(wù)器驗(yàn)證登錄成功以后會(huì)默認(rèn)重定向至配置的SuccessUrl。這樣并不能將頁面跳轉(zhuǎn)回用戶的原來請求的頁面。所以我重寫了CasFilter中的issueSuccessRedirect達(dá)到這個(gè)目的

/**
 * <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");//前端頁面在請求的時(shí)候在header中帶上請求這個(gè)接口的url。這樣便將登錄成功后需要跳轉(zhuǎn)的地址綁定到了對應(yīng)的Subject對象中。以便于在登錄以后跳轉(zhuǎn)到這個(gè)頁面
  if (StringUtil.isBlank(successUrl)) {
   WebUtils.redirectToSavedRequest(request, response, getSuccessUrl());
  } else {
   WebUtils.redirectToSavedRequest(request, response, successUrl);
  }
 }
}

#4.用戶安全的退出

后期發(fā)現(xiàn)直接依靠原有的logout會(huì)發(fā)生session未注銷的情況。所以重寫了LogoutFilter。登出的時(shí)候直接調(diào)用配置的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;
 }
}

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI