一、cas原理分析

SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。CAS是一种基于http协议的B/S应用系统单点登录实现方案,认识CAS之前首先要熟悉http协议、Session与Cookie等Web开发基本知识。

1.1 Cas登录:
两次前端跳转、一次后端验证

1.1.1 首次访问应用A

第一次跳转:

客户端访问应用系统A,应用系统判断Session发现未登录,返回302跳转到sso登录页面,并传递service参数给sso,该service参数有两个作用:(回跳、认证)
1、 service一般传递应用系统url地址,用于sso认证通过后回跳到应用系统A;
2、service参数同时会被cas服务端的作为cas客户端的唯一标记记录下来,用于后期匹配相应的认证凭据;

第二次跳转:

浏览器显示登录页面,用户输入账号密码登录成功后,sso会返回302跳转回到原来请求的应用系统页面,并携带ticket参数,作为认证票据,同时通过Set-Cookie向浏览器记录TGT,(TGT的作用将在下一个应用系统需要登录的时候体现出作用,是避免重复登录的关键)

后台进行一次票据验证:

应用系统接收到带有ticket的请求后,从后台直接向sso服务器发起一个http请求,将service和ticket作为参数,用于验证ticket的有效性;如果ticket有效,sso服务器将返回该ticket对应的登录用户名。

图例:


1.1.2 访问A登陆后首次访问应用B

访问应用B系统,根据session判断未登录,重定向到CAS Serve,根据Cookie里面的TGT找到对应的用户信息,携带ticket重定向会应用系统B,应用系统B接收到带有ticket的请求后,从后台直接向sso服务器发起一个http请求,将service和ticket作为参数,用于验证ticket的有效性;如果ticket有效,sso服务器将返回该ticket对应的登录用户名

二、实现cas客户端

yaml配置:

security:cas:server:host: https://localhost:8443/cas  #cas服务器的地址login: ${security.cas.server.host}/login  #单点登录地址logout: ${security.cas.server.host}/logout  #单点登出店址service:webHost: http://localhost:8090  #应用系统的地址login: /login       #应用系统的登录入口logout: /logout     #应用系统的登出入口

2.1 参数配置类:

  • CAS认证中心参数配置类
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;@Data
@Configuration
public class CasServerConfig {@Value("${security.cas.server.host}")private String host;@Value("${security.cas.server.login}")private String login;@Value("${security.cas.server.logout}")private String logout;
}
  • 应用服务器参数配置类
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;@Data
@Configuration
public class CasServiceConfig {@Value("${security.cas.service.webHost}")private String webHost;@Value("${security.cas.service.login}")private String login;@Value("${security.cas.service.logout}")private String logout;private Boolean sendRenew = false;}

2.2 CAS配置

  • CAS配置类
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.web.authentication.logout.LogoutFilter;@Configuration
public class SecurityConfiguration {@Autowiredprivate CasServerConfig casServerConfig;@Autowired private CasServiceConfig casServiceConfig;@Beanpublic ServiceProperties serviceProperties() {ServiceProperties serviceProperties = new ServiceProperties();serviceProperties.setService(this.casServiceConfig.getWebHost() + this.casServiceConfig.getLogin()); //回跳地址serviceProperties.setSendRenew(this.casServiceConfig.getSendRenew());//是否敏感,即登录不同的应用系统是否还要重新登录serviceProperties.setAuthenticateAllArtifacts(true); //是否对没有ticket的访问需要验证return serviceProperties;}/**** CAS认证过滤器* @param authenticationManager* @param serviceProperties* @return*/@Beanpublic CasAuthenticationFilter casAuthenticationFilter(AuthenticationManager authenticationManager, ServiceProperties serviceProperties) {CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();casAuthenticationFilter.setAuthenticationManager(authenticationManager);casAuthenticationFilter.setServiceProperties(serviceProperties);casAuthenticationFilter.setFilterProcessesUrl(this.casServiceConfig.getLogin());casAuthenticationFilter.setContinueChainBeforeSuccessfulAuthentication(false);casAuthenticationFilter.setAuthenticationSuccessHandler(new UrlAuthenticationSuccessHandler("/"));return casAuthenticationFilter;}/*** CAS入口* @param serviceProperties* @return*/@Beanpublic CasAuthenticationEntryPoint casAuthenticationEntryPoint(ServiceProperties serviceProperties) {CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint();entryPoint.setLoginUrl(this.casServerConfig.getLogin());entryPoint.setServiceProperties(serviceProperties);return entryPoint;}/*** CASticket验证* @return*/@Beanpublic Cas20ServiceTicketValidator cas20ServiceTicketValidator() {return new Cas20ServiceTicketValidator(this.casServerConfig.getHost());}@Beanpublic CasAuthenticationProvider casAuthenticationProvider(AuthenticationUserDetailsService<CasAssertionAuthenticationToken> userDetailsService,ServiceProperties serviceProperties,Cas20ServiceTicketValidator ticketValidator) {CasAuthenticationProvider provider = new CasAuthenticationProvider();provider.setKey("casProvider");provider.setServiceProperties(serviceProperties);provider.setTicketValidator(ticketValidator);provider.setAuthenticationUserDetailsService(userDetailsService);//自己的userDetailServicereturn provider;}/*** 登出过滤器* @return*/@Beanpublic LogoutFilter logoutFilter() {String logoutRedirectPath =this.casServerConfig.getLogout() + "?service=" + this.casServiceConfig.getWebHost();LogoutFilter logoutFilter =new LogoutFilter(logoutRedirectPath, new CasSecurityContextLogoutHandler());logoutFilter.setFilterProcessesUrl(this.casServiceConfig.getLogout());return logoutFilter;}
}
  • 认证成功处理类
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.util.StringUtils;import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Collection;@Slf4j(topic = "c.successHandler")
public class UrlAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {public UrlAuthenticationSuccessHandler() {super();}public UrlAuthenticationSuccessHandler(String defaultTargetUrl) {super(defaultTargetUrl);}/*** 认证成功后* @param request* @param response* @param authentication* @throws IOException* @throws ServletException*/@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {super.onAuthenticationSuccess(request, response, authentication);}/***返回一个认证成功后的路径* @param request* @param response* @return*/@Overrideprotected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {StringBuffer targetUrl = null;//根据自己需求写HttpSession session = request.getSession();targetUrl = (StringBuffer) session.getAttribute("REQUEST_URL");log.debug("目标路径为{}", targetUrl);return targetUrl.toString();}
}
  • 自定义过滤器(存储要访问的路径)
mport com.hzx.hzxy_grid.domain.SecurityUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Collection;@Component
@Slf4j
public class HttpParamsFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)throws IOException, ServletException {final HttpServletRequest request = (HttpServletRequest) servletRequest;final HttpServletResponse response = (HttpServletResponse) servletResponse;HttpSession session = request.getSession();StringBuffer requestURL = request.getRequestURL();log.info("请求地址:" + requestURL);chain.doFilter(request, response);}@Overridepublic void destroy() {}
}
  • 单点登出过滤器
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.util.Assert;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;public class CasSecurityContextLogoutHandler implements LogoutHandler {protected final Log logger = LogFactory.getLog(this.getClass());private boolean invalidateHttpSession = true;private boolean clearAuthentication = true;public CasSecurityContextLogoutHandler() {}public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {Assert.notNull(request, "HttpServletRequest required");if (this.invalidateHttpSession) {HttpSession session = request.getSession(false);if (session != null) {this.logger.debug("Invalidating session: " + session.getId());//发布退出登录事件,自己增加,监听此事件处理一些事情session.invalidate(); //session失效
//        LogoutEvent event = new LogoutEvent();
//        event.setSessionId(session.getId());
//        EventPublisherUtil.publish(event);}}if (this.clearAuthentication) {SecurityContext context = SecurityContextHolder.getContext();context.setAuthentication((Authentication)null);}SecurityContextHolder.clearContext();}public boolean isInvalidateHttpSession() {return this.invalidateHttpSession;}public void setInvalidateHttpSession(boolean invalidateHttpSession) {this.invalidateHttpSession = invalidateHttpSession;}public void setClearAuthentication(boolean clearAuthentication) {this.clearAuthentication = clearAuthentication;}
}
  • springsecurity配置类
import com.hzx.hzxy_grid.config.custom.CustomFilter;
import com.hzx.hzxy_grid.config.custom.CustomUrlDecisionManager;
import com.hzx.hzxy_grid.handler.CustomizeAccessDeniedHandler;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.cors.CorsUtils;@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER)
public class CasWebSecurityConfiguration extends WebSecurityConfigurerAdapter {@Autowiredprivate CasAuthenticationEntryPoint casAuthenticationEntryPoint;@Autowiredprivate CasAuthenticationProvider casAuthenticationProvider;@Autowiredprivate CasAuthenticationFilter casAuthenticationFilter;@Autowiredprivate LogoutFilter logoutFilter;@Autowiredprivate CasServerConfig casServerConfig;@Autowiredprivate HttpParamsFilter httpParamsFilter;@Autowiredprivate CustomFilter customFilter;@Autowiredprivate CustomUrlDecisionManager customUrlDecisionManager;@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/swagger-resources/**","/swagger-ui.html","/v2/api-docs","/webjars/**","/v3/api-docs",
//                "/login","/static/**","/api/**");super.configure(web);}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.headers().frameOptions().disable();http.csrf().disable();http.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll().anyRequest().authenticated(); // 所有资源都需要登陆后才可以访问。http.logout().permitAll(); // 不拦截注销http.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint).accessDeniedHandler(new CustomizeAccessDeniedHandler());// 单点注销的过滤器,必须配置在SpringSecurity的过滤器链中,如果直接配置在Web容器中,貌似是不起作用的。我自己的是不起作用的。SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();singleSignOutFilter.setArtifactParameterName(this.casServerConfig.getHost());http.addFilter(casAuthenticationFilter).addFilterBefore(logoutFilter, LogoutFilter.class).addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class);http.addFilterBefore(httpParamsFilter, FilterSecurityInterceptor.class);http.addFilterBefore(filterSecurityInterceptor(), FilterSecurityInterceptor.class);http.antMatcher("/**");}/*** 权限拦截** @return* @throws Exception*/public FilterSecurityInterceptor filterSecurityInterceptor() throws Exception {FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();filterSecurityInterceptor.setSecurityMetadataSource(customFilter);filterSecurityInterceptor.setAuthenticationManager(authenticationManager());filterSecurityInterceptor.setAccessDecisionManager(customUrlDecisionManager);return filterSecurityInterceptor;}@Autowiredpublic void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {auth.authenticationProvider(casAuthenticationProvider);}@Beanpublic ServletListenerRegistrationBean<SingleSignOutHttpSessionListener>singleSignOutHttpSessionListener() {ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> servletListenerRegistrationBean =new ServletListenerRegistrationBean<>();servletListenerRegistrationBean.setListener(new SingleSignOutHttpSessionListener());return servletListenerRegistrationBean;}/*** AuthenticationManager** @return* @throws Exception*/@Bean(name = BeanIds.AUTHENTICATION_MANAGER)@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}
  • CustomFilter类
/*** 根据访问的路径判断出所需的角色*/@Component
@Slf4j(topic = "c.CustomUrl")
public class CustomFilter implements FilterInvocationSecurityMetadataSource {//路径匹配private AntPathMatcher antPathMatcher = new AntPathMatcher();@Autowiredprivate MenuService menuService;@Overridepublic Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {// 获取请求的UrlString requestUrl = ((FilterInvocation) o).getRequestUrl();List<Menu> menuWithRoles = menuService.getMenuWithRoles();for (Menu menu : menuWithRoles) {//访问的路径是否跟菜单的里的路径相同if (antPathMatcher.match(menu.getUrl(), requestUrl)) {String[] roles = menu.getRoleList().stream().map(Role::getRoleName).toArray(String[]::new);log.info("需要的权限为{}", roles);System.out.println(roles);return SecurityConfig.createList(roles);}}return SecurityConfig.createList("ROLE_LOGIN");}@Overridepublic Collection<ConfigAttribute> getAllConfigAttributes() {return null;}@Overridepublic boolean supports(Class<?> aClass) {return false;}
}
  • CustomDecisionManager
@Component
public class CustomUrlDecisionManager implements AccessDecisionManager {@Overridepublic void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {for (ConfigAttribute configAttribute : collection) {String needRole = configAttribute.getAttribute();if ("ROLE_LOGIN".equals(needRole)) {if (authentication instanceof AnonymousAuthenticationToken) {throw new AccessDeniedException("用户未登录,请登录");} elsereturn;}//判断是否有对应的权限信息Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();for (GrantedAuthority authority : authorities) {if(authority.getAuthority().equals(needRole))return;}}throw new AccessDeniedException("用户无权限访问");}@Overridepublic boolean supports(ConfigAttribute configAttribute) {return false;}@Overridepublic boolean supports(Class<?> aClass) {return false;}
}
  • 未授权无法访问CustomizeAccessDeniedHandler
import com.hzx.hzxy_grid.domain.R;
import com.hzx.hzxy_grid.enums.Status;
import com.hzx.hzxy_grid.util.ResponseUtil;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** 未授权,用户无权访问*/public class CustomizeAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {ResponseUtil.out(httpServletResponse,R.error().status(Status.UNAUTHORIZED));}
}
  • AuthenticationUserDetailsService
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hzx.hzxy_grid.domain.Admin;
import com.hzx.hzxy_grid.domain.Role;
import com.hzx.hzxy_grid.domain.SecurityUser;
import com.hzx.hzxy_grid.mapper.AdminMapper;
import com.hzx.hzxy_grid.mapper.RoleMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;@Slf4j(topic = "c.UserDetailServiceImpl")
@Service
public class MyUserDetailServiceImpl implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {@Autowiredprivate RoleMapper roleMapper;@Autowiredprivate AdminMapper adminMapper;@Autowiredprivate RedisTemplate redisTemplate;@Overridepublic UserDetails loadUserDetails(CasAssertionAuthenticationToken token) throws UsernameNotFoundException {String username = token.getName();Admin admin = adminMapper.selectOne(new LambdaQueryWrapper<Admin>().eq(Admin::getUsername, username));log.info("admin:{}", admin);if (admin == null)throw new UsernameNotFoundException("用户名不存在");SecurityUser securityUser = new SecurityUser();List<String> permissionList = null;//如果redis不存在该用户的权限if (!redisTemplate.hasKey("permission_" + admin.getUid())) {//添加权限List<Role> roleList = roleMapper.getRolesByAdminId(admin.getUid());permissionList = roleList.stream().map(role -> role.getRoleName()).collect(Collectors.toList());securityUser.setPermissionValueList(permissionList);securityUser.setCurrentUserInfo(admin);log.info("从数据库中查询到用户的权限为{}", permissionList);} else {permissionList = (List<String>) redisTemplate.opsForValue().get("permission_" + admin.getUid());log.info("从redis中查询到用户的权限为{}",permissionList);}securityUser.setPermissionValueList(permissionList);securityUser.setCurrentUserInfo(admin);log.info("用户的权限为{}", permissionList);return securityUser;}
}
  • SecurityUser
import lombok.Data;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;@Data
public class SecurityUser implements UserDetails, CredentialsContainer {//当前登录用户private transient Admin currentUserInfo;//当前权限private List<String> permissionValueList;public SecurityUser() {}public SecurityUser(Admin admin) {if (admin != null) {this.currentUserInfo = admin;}}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {Collection<GrantedAuthority> authorities = new ArrayList<>();for(String permissionValue : permissionValueList) {if(StringUtils.isEmpty(permissionValue)) continue;SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);authorities.add(authority);}return authorities;}@Overridepublic String getPassword() {return currentUserInfo.getPassword();}@Overridepublic String getUsername() {return currentUserInfo.getUsername();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}@Overridepublic void eraseCredentials() {}
}

cas单点登录原理与实现(整合springsecurity)相关推荐

  1. CAS单点登录原理解析及相关配置

    1.基于Cookie的单点登录的回顾 基于Cookie的单点登录核心原理: 将用户名密码加密之后存于Cookie中,之后访问网站时在过滤器(filter)中校验用户权限,如果没有权限则从Cookie中 ...

  2. cas单点登录原理碎碎念

    2019独角兽企业重金招聘Python工程师标准>>> 也许有一天,你去面试,当面试官问你做cas的经验的时候,你会怎么描述? cas单点登录的原理? 单点登录是分为基于sessio ...

  3. CAS单点登录原理简单介绍

    1. SSO简介 1.1 单点登录定义 单点登录(Single sign on),英文名称缩写SSO,SSO的意思就是在多系统的环境中,登录单方系统,就可以在不用再次登录的情况下访问相关受信任的系统. ...

  4. CAS教程-第一篇-CAS单点登录原理解析

    转载自:http ://www.cnblogs.com/lihuidu/p/6495247.html 1,基于饼干的单点登录的回顾 基于Cookie的的单点登录核心原理: 将用户名密码加密之后存于饼干 ...

  5. CAS单点登录原理(包含详细流程,讲得很透彻,耐心看下去一定能看明白!)

    一.CAS简介和整体流程 CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目.CAS ...

  6. CAS单点登录的实现(二)

    这篇文章对CAS单点登录具体实现的一些步骤就行讲述,至于CAS单点登录的实现原理分析,请参看下面这篇文章: CAS单点登录原理分析(一) https://blog.csdn.net/qq_412582 ...

  7. CAS单点登录的实现

    这篇文章对CAS单点登录具体实现的一些步骤就行讲述,至于CAS单点登录的实现原理分析,请参看下面这篇文章: CAS单点登录原理分析(一) https://blog.csdn.net/qq_412582 ...

  8. cas跨域单点登录原理_CAS实现SSO单点登录原理

    1.      CAS 简介 1.1.  What is CAS ? CAS ( Central Authentication Service ) 是 Yale 大学发起的一个企业级的.开源的项目,旨 ...

  9. 爆破专栏丨Spring Security系列教程之实现CAS单点登录上篇-概述

    作者:千锋一一哥 前言 从本章节开始,一一哥 会给各位讲解一个很常见也很重要的知识点,就是单点登录!现在的大型分布式项目,基本都会考虑实现单点登录,而且现在网上也有很多单点登录的实现方案.开源项目,但 ...

最新文章

  1. hdu1728 广搜
  2. db2 mysql sql server_下文给大家介绍mysql数据库介绍,具体详情如下所示:MySQL数据库:Oracle、DB2、SQL Server、MySQL、access、mang...
  3. 个推mastersecret_推送:个推
  4. Lighttpd、Nginx 、Apache 隐藏响应头信息的Server信息和版本信息
  5. curl返回常见错误码
  6. TensorFlow入门:mnist数据集解析
  7. 记录一次pl2303串口驱动解决故障
  8. 【正点原子MP157连载】第四章 ATK-STM32MP157功能测试-摘自【正点原子】STM32MP157快速体验
  9. secureCRT免密码登陆Linux
  10. gromacs 安装_安装gromacs的一些心得
  11. 华为联合全球伙伴加速上线HMS内容类应用,影音娱乐体验一步尝鲜
  12. 必知的Python 五个精彩彩蛋
  13. 便签数据怎么恢复 有能恢复手机便签数据的方法吗
  14. 图像处理:利用相似度处理相似度较大的图像
  15. 体育测试一般测什么软件,体能测试有哪些项目 体能测试具体测什么
  16. ArcGIS for JavaScript开发Cluster思路解析
  17. Java 学习笔记:第一章 Java入门
  18. JAVA进阶案例 TCP编程之网络聊天工具(服务端)
  19. 【大数据应用篇】案例解析—直击五个典型的大数据应用示范
  20. 爬取google scholar数据

热门文章

  1. PL/SQL developer 导入Excel数据到linux安装的Oracle中文乱码问题
  2. Qualifier注解
  3. hive on tez学习之官网和源码
  4. Tez安装与使用(与hive配合使用)及lzo.jar报错错误解决
  5. html点击按钮弹出悬浮窗_JS控制弹出悬浮窗口(一览画面)的实例代码
  6. python word2vec库_python word2vec的使用
  7. 大数据帮助你提高销售业绩的4种方式
  8. 无需下载软件怎么将多张图片组合成一张图片
  9. @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited注解
  10. 苹果手表对比_苹果会手表洗手提醒真正起作用吗