1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > shiro+cas 前后端分离 单点登录 解决方案

shiro+cas 前后端分离 单点登录 解决方案

时间:2019-06-21 13:00:53

相关推荐

shiro+cas 前后端分离 单点登录 解决方案

shiro+cas 前后端分离 单点登录 解决方案

写在前面主要的重点源码 cas服务cas客户端单点登录实现重定向问题测试遇到一个奇怪的问题

写在前面

在设计开发自己的博客系统时,选择cas+shiro做用户认证和鉴权的框架,实现单点登录,方便接入其他系统。

曾经在徐州客户现场被cas折磨好久,一直想把cas啃下来。原谅我是个小菜鸟

搭建这个框架爬了很多坑,也参考了很多大佬的博客,自己慢慢试,跟踪源码,慢慢思考,也有一点收获。

目前项目还没有完成,可能还有很多坑,有很多地方处理的不是很好,现在只是流程通了,先记录下来,省着时间长忘了思路。也请各位大佬帮忙之处不对的地方

主要的重点

跨域问题重定向sessionid的保持cookie的 domain和path

源码

cas服务器

/jsong93/cas-server

客户端

/jsong93/shiro-client

cas服务

cas服务端搭建

cas md5+盐加密

cas客户端

spring boot cas+shiro环境搭建

单点登录实现

shiroconfig配置

package com.jsong.wiki.backend.config;import com.jsong.wiki.backend.bean.ShiroUrl;import com.jsong.wiki.backend.filter.MyAuthenticationFilter;import com.jsong.wiki.backend.filter.MyFormAuthenticationFilter;import com.jsong.wiki.backend.filter.MyUserFilter;import com.jsong.wiki.backend.shiro.ShiroCasRealm;import org.apache.shiro.cache.ehcache.EhCacheManager;import org.apache.shiro.cas.CasFilter;import org.apache.shiro.cas.CasSubjectFactory;import org.apache.shiro.mgt.SecurityManager;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.filter.authc.LogoutFilter;import org.apache.shiro.web.filter.authc.UserFilter;import org.apache.shiro.web.mgt.DefaultWebSecurityManager;import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import javax.servlet.Filter;import java.util.HashMap;import java.util.Map;/*** shiro配置** @Author: Jsong* @Date: /2/7 21:58* @Description:*/@Configurationpublic class ShiroConfig {// shiroFilter@Beanpublic ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager,@Qualifier("casFilter") MyAuthenticationFilter casFilter,//@Qualifier("logoutFilter") LogoutFilter logoutFilter,@Qualifier("userFilter") MyUserFilter userFilter,MyFormAuthenticationFilter myFormAuthenticationFilter,ShiroUrl shiroUrl) {ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);// 设置登录URL,当用户未认证,但访问了需要认证后才能访问的页面,就会自动重定向到登录URLshiroFilterFactoryBean.setLoginUrl(shiroUrl.getLoginUrl());shiroFilterFactoryBean.setSuccessUrl(shiroUrl.getSuccessUrl());// 设置没有权限的URL,当用户认证后,访问一个页面却没有权限时,就会自动重定向到没有权限的URL,若用户未认证访问一个需要权限的URL时,会跳转到登录URLshiroFilterFactoryBean.setUnauthorizedUrl(shiroUrl.getUnauthorizedUrl());Map<String, Filter> filters = new HashMap<>();// org.apache.shiro.web.filter.mgt.DefaultFilter 包含所有的过滤器filters.put("casFilter", casFilter);filters.put("userFilter", userFilter);filters.put("authc", myFormAuthenticationFilter);// filters.put("logoutFilter", logoutFilter);// 将Filter添加到Shiro过滤器链中,用于对资源设置权限shiroFilterFactoryBean.setFilters(filters);Map<String, String> filterChainDefinitionMap = new HashMap<String, String>();filterChainDefinitionMap.put(shiroUrl.getCasFilterUrlPattern(), "casFilter");// filterChainDefinitionMap.put(shiroUrl.getCasFilterUrlPattern(), "userFilter");// filterChainDefinitionMap.put(shiroUrl.getLogoutUrlPattern(), "logoutFilter");filterChainDefinitionMap.putAll(shiroUrl.getAuthUrlPatternMap());// 配置哪些请求需要受保护,以及访问这些页面需要的权限shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}// 认证filter@Beanpublic MyAuthenticationFilter casFilter(ShiroUrl shiroUrl) {MyAuthenticationFilter casFilter = new MyAuthenticationFilter();// 登录成功urlcasFilter.setSuccessUrl(shiroUrl.getSuccessUrl());// 登录失败urlcasFilter.setFailureUrl(shiroUrl.getFailureUrl());return casFilter;}/**** 配置登录后重定向filter* @date /2/29 20:41* @author Jsong* @param* @return com.jsong.wiki.backend.filter.MyUserFilter*/@Beanpublic MyUserFilter userFilter(){return new MyUserFilter();}@Beanpublic MyFormAuthenticationFilter myFormAuthenticationFilter(){return new MyFormAuthenticationFilter();}// 自定义 casRealm@Beanpublic ShiroCasRealm casRealm(ShiroUrl shiroUrl, EhCacheManager ehCacheManager) {ShiroCasRealm casRealm = new ShiroCasRealm();// cas服务器casRealm.setCasServerUrlPrefix(shiroUrl.getCas().getServerUrlPrefix());// 客户端地址,用于接收tiketcasRealm.setCasService(shiroUrl.getCas().getService());casRealm.setCacheManager(ehCacheManager);return casRealm;}/*** 问题?????* 把这个bean注入后,登录后就会重定向到登出,为什么呢?????** @param shiroUrl* @return*/// @Beanpublic LogoutFilter logoutFilter(ShiroUrl shiroUrl) {LogoutFilter logoutFilter = new LogoutFilter();// 登出后重定向地址logoutFilter.setRedirectUrl(shiroUrl.getLogoutUrl());return logoutFilter;}@Beanpublic EhCacheManager ehCacheManager() {EhCacheManager ehCacheManager = new EhCacheManager();ehCacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");return ehCacheManager;}// 配置securityManager SecurityManager,Shiro的安全管理,主要是身份认证的管理,缓存管理,cookie管理@Beanpublic SecurityManager securityManager(ShiroCasRealm casRealm, EhCacheManager ehCacheManager) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setSubjectFactory(new CasSubjectFactory());securityManager.setCacheManager(ehCacheManager);securityManager.setRealm(casRealm);return securityManager;}// 配置lifecycleBeanPostProcessor,shiro bean的生命周期管理器,可以自动调用Spring IOC容器中shiro bean的生命周期方法(初始化/销毁)// @Beanpublic LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}/* 为了支持Shiro的注解需要定义DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor两个bean配置DefaultAdvisorAutoProxyCreator,必须配置了lifecycleBeanPostProcessor才能使用*/// @DependsOn("lifecycleBeanPostProcessor")// @Beanpublic DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();return defaultAdvisorAutoProxyCreator;}// 配置AuthorizationAttributeSourceAdvisor@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}}

重写CasFilter onAccessDenied方法

解决的问题

请求重定向返回cas登录页面的字符串前端重定向,开启了一个新的session,shiro无法通过上次请求保存的saverequest中获取请求的uri,以至登录成功后,无法返回请求的页面

非前后端分离的项目,当请求一个需要权限的方法时,需要servlet可以直接重定向到cas登录页面

重定向问题

前后端分离的项目,当使用ajax请求一个需要权限的方法时,也会重定向,但会直接将重定向的cas登录页面以字符串的方式返回

解决办法

重写FormAuthenticationFilter的onAccessDenied方法,通过subject.isAuthenticated()判断是否登录,如果未登录返回给前端401,前端在重定向到cas登录页面

package com.jsong.wiki.backend.filter;import com.jsong.wiki.backend.bean.CookieProperties;import lombok.extern.log4j.Log4j2;import org.apache.shiro.SecurityUtils;import org.apache.shiro.subject.Subject;import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;import org.apache.shiro.web.util.WebUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.ponent;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/*** authc 的过滤器** @Author: Jsong* @Date: /3/2 22:24* @Description:*///@Component@Log4j2public class MyFormAuthenticationFilter extends FormAuthenticationFilter {@Value("${shiro.loginUrl}")private String loginUrl;@Value("${front.url}")private String frontUrl;// protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {// log.info("-----------------redirect login --------------------");// Cookie cookie = new Cookie("front-url", ((HttpServletRequest) request).getHeader("front-url"));// ((HttpServletResponse) response).addCookie(cookie);// saveRequest(request);// redirectToLogin(request, response);// }@Autowiredprivate CookieProperties cookieProperties;/****重写登录重定向* 把请求内容存入cookie,重定向的时候带着cookie保存状态* @date /3/3 14:46* @author Jsong* @param request* @param response* @return boolean*/@Overrideprotected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {if (isLoginRequest(request, response)) {if (isLoginSubmission(request, response)) {if (log.isTraceEnabled()) {log.trace("Login submission detected. Attempting to execute login.");}return executeLogin(request, response);} else {if (log.isTraceEnabled()) {log.trace("Login page view.");}//allow them to see the login page ;)return true;}} else {if (log.isTraceEnabled()) {log.trace("Attempting to access a path which requires authentication. Forwarding to the " +"Authentication url [" + getLoginUrl() + "]");}log.info("-----------------redirect login --------------------");// Cookie[] cookies = ((HttpServletRequest) request).getCookies();// for (Cookie cookie : cookies) {//cookie.setDomain(cookieProperties.getDomain());//cookie.setPath(cookieProperties.getPath());//cookie.setHttpOnly(cookieProperties.getHttpOnly());// }// Cookie cookie = new Cookie(frontUrl, ((HttpServletRequest) request).getHeader(frontUrl));// cookie.setDomain(cookieProperties.getDomain());// cookie.setPath(cookieProperties.getPath());// cookie.setHttpOnly(cookieProperties.getHttpOnly());// ((HttpServletResponse) response).addCookie(cookie);// saveRequestAndRedirectToLogin(request, response);saveRequest(request);// redirectToLogin(request, response);// 读取到的 /index.jsp 不知道为啥// String loginUrl = getLoginUrl();// shiro 会把请求内容保存起来,未登录为空// if(WebUtils.getSavedRequest(request)==null){//// 未授权,返回前端状态401 进行重定向// request里获取不到用户信息,不能通过request判断是否登录,不能用这个方法了// ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);// return false;// }Subject subject = SecurityUtils.getSubject();if (!subject.isAuthenticated()) {// 未授权,返回前端状态401 进行重定向((HttpServletResponse) response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);return false;}// ajax 重定向会返回html页面字符串WebUtils.issueRedirect(request, response, loginUrl);return false;}}}

/** @Descripttion:* @version:* @Author: jsong* @Date: -02-25 22:30:54* @LastEditors: jsong* @LastEditTime: -03-07 12:04:18*/import axios from "axios";import Cookies from "js-cookie";axios.defaults.withCredentials = true;const baseUrl = "/blog-backend";export const api = axios.create({// headers: { "Access-Control-Allow-Origin": "http://127.0.0.1:28080" },// baseUrl});api.interceptors.request.use(req => {req.withCredentials = true;console.log("req", req);// Cookies.set(// "api-uri",// req.url.indexOf(baseUrl) > -1 ? req.url.substr(13) : null// );Cookies.set("front-url", window.location.href);// req.headers["api-uri"] =// req.url.indexOf(baseUrl) > -1 ? req.url.substr(13) : null;// req.headers["front-url"] = window.location.href;return req;},err => {console.log("e", err);if (401 === err.request.status) {}});api.interceptors.response.use(res => {// if (res && res.request) {// const responseURL = res.request.responseURL;// if (//responseURL ===//"http://127.0.0.1:18080/cas/login?service=http://127.0.0.1:18081/blog-backend/shiro-cas"// ) {// const jsessionid = Cookies.get("JSESSIONID");// console.log("jssionid", jsessionid);// localStorage.setItem("jsessionid", jsessionid);// window.location.href = `${responseURL}&test=1`;// window.location.href = `${responseURL}`;// }// console.log("cookie", document.cookie);// return res;// }return res;},err => {console.log("e", err);if (401 === err.response.status) {const jsessionid = Cookies.get("JSESSIONID");console.log("jssionid", jsessionid);localStorage.setItem("jsessionid", jsessionid);window.location.href ="http://127.0.0.1:18080/cas/login?service=http://127.0.0.1:18081/blog-backend/shiro-cas";}});

## 时序图用户未登录```mermaidsequenceDiagram页面 ->> shiro客户端: 请求查询接口Note right of shiro客户端: 没有认证AuthenticationFilter.isAuthenticated() false<br/>FormAuthenticationFilter.onAccessDenied() 被拒绝<br/>保存请求信息AccessControlFilter.saveRequest() 保存请求信息<br/>AccessControlFilter.redirectToLogin() 重定向到登录页面<br/>shiro客户端 -->> 页面: 页面 ->> cas服务: 重定向到cas服务 登录Note right of cas服务: 获取TGC ST<br/>登录cas服务 -->> 页面: 页面 ->> shiro客户端: 携带STNote right of shiro客户端: 根据ST创建token<br>AuthenticatingFilter.createToken()<br/>重定向到前端页面shiro客户端 -->> 页面:

用户已登录

测试

在csdn模仿简书 还是简书好看 哈哈哈

遇到一个奇怪的问题

登录成功后重定向到前端的url是字符串时,下次访问不会再去cas server重新认证

当url是@Value获取的值时,下次访问会到cas server重新认证

详细问题

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。