欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

SAML单点登录-spring-security-saml客户端SP

时间:2023-06-30
SAML单点登录-spring-security-saml客户端SP

使用spring-security-saml搭建SAML协议的客户端,该依赖是spring框架的官方库,配置方便、文档详细。提供了包括单点登录、单点登出、获取sq元数据文件等接口,无需自己实现,参考:spring-security-saml与应用程序的集成

SpringMVC接入 Maven添加spring-security-saml依赖

org.springframework.security.extensionsspring-security-saml2-core1.0.10.RELEASE

配置文件中添加客户端相关配置

# 认证中心服务信息 -> IDP元数据URLsp.idpmetadataUrl=http://192.168.59.117:30030/gc-starter-ac/idp/metadata# entityId,服务提供商唯一标识sp.entityId=cas:saml:sp:springboot# 是否签名断言,则需要在idp上传sp的证书/公钥文件以供解密sp.wantAssertionSigned=false# 是否签名元数据sp.signmetadata=false# 签名算法sp.signAlg=http://www.w3.org/2001/04/xmldsig-more#rsa-sha256# 是否启用服务发现。一个sp可以配置多个idp,启动服务发现允许进入idp选择页面选择idp,如果不启用的话默认使用idp列表的第一个sp.idpDiscoveryEnable=true# 服务发现选择页面路由sp.IdpSelectionPath=/saml/discovery# idp登录成功后的重定向的页面路由,也就是首页路由sp.successLoginUrl=/landing# idp登录失败后的重定向的页面路由sp.failLoginUrl=/error# 登出成功后跳转的页面路由sp.successLogoutUrl=/# jks文件位置sp.jks.path=classpath:/saml/samlKeystore.jks# jks密码sp.jks.password=nalle123# 默认密钥sp.jks.defaultKey=apollo

通过xml文件的方式配置bean,classpath下新建securityContext.xml

<?xml version="1.0" encoding="UTF-8" ?> ${sp.idpmetadataUrl} 5000 passwords = new HashMap(); passwords.put(spConfig.getJks().getDefaultKey(), spConfig.getJks().getPassword()); String defaultKey = spConfig.getJks().getDefaultKey(); return new JKSKeyManager(storeFile, storePass, passwords, defaultKey); } @Bean public WebSSOProfileOptions defaultWebSSOProfileOptions() { WebSSOProfileOptions webSSOProfileOptions = new WebSSOProfileOptions(); webSSOProfileOptions.setIncludeScoping(false); return webSSOProfileOptions; } // Entry point to initialize authentication, default values taken from // properties file @Bean public SAMLEntryPoint samlEntryPoint() { SAMLEntryPoint samlEntryPoint = new SAMLEntryPoint(); samlEntryPoint.setDefaultProfileOptions(defaultWebSSOProfileOptions()); return samlEntryPoint; } // 扩展元数据 // Setup advanced info about metadata @Bean public Extendedmetadata extendedmetadata() { Extendedmetadata extendedmetadata = new Extendedmetadata(); extendedmetadata.setIdpDiscoveryEnabled(spConfig.getIdpDiscoveryEnable()); extendedmetadata.setSigningAlgorithm(spConfig.getSignAlg()); extendedmetadata.setSignmetadata(spConfig.getSignmetadata()); extendedmetadata.setEcpEnabled(true); return extendedmetadata; } // 服务发现页面地址 // IDP Discovery Service @Bean public SAMLDiscovery samlIDPDiscovery() { SAMLDiscovery idpDiscovery = new SAMLDiscovery(); idpDiscovery.setIdpSelectionPath(spConfig.getIdpSelectionPath()); return idpDiscovery; } @Bean@Qualifier("idp-ssocircle")public ExtendedmetadataDelegate ssoCircleExtendedmetadataProvider()throws metadataProviderException {String idpSSOCirclemetadataURL = spConfig.getIdpmetadataUrl();HTTPmetadataProvider httpmetadataProvider = new HTTPmetadataProvider(this.backgroundTaskTimer, httpClient(), idpSSOCirclemetadataURL);httpmetadataProvider.setParserPool(parserPool());ExtendedmetadataDelegate extendedmetadataDelegate = new ExtendedmetadataDelegate(httpmetadataProvider, extendedmetadata());extendedmetadataDelegate.setmetadataTrustCheck(false);extendedmetadataDelegate.setmetadataRequireSignature(false);backgroundTaskTimer.purge();return extendedmetadataDelegate;} // IDP metadata configuration - paths to metadata of IDPs in circle of trust // is here // Do no forget to call iniitalize method on providers @Bean @Qualifier("metadata") public CachingmetadataManager metadata() throws metadataProviderException { List providers = new ArrayList(); providers.add(ssoCircleExtendedmetadataProvider()); return new CachingmetadataManager(providers); } // 元数据生成bean // Filter automatically generates default SP metadata @Bean public metadataGenerator metadataGenerator() { metadataGenerator metadataGenerator = new metadataGenerator(); metadataGenerator.setEntityId(spConfig.getEntityId()); metadataGenerator.setExtendedmetadata(extendedmetadata()); metadataGenerator.setIncludeDiscoveryExtension(false); metadataGenerator.setKeyManager(keyManager()); metadataGenerator.setWantAssertionSigned(spConfig.getWantAssertionSigned()); return metadataGenerator; } // The filter is waiting for connections on URL suffixed with filterSuffix // and presents SP metadata there @Bean public metadataDisplayFilter metadataDisplayFilter() { return new metadataDisplayFilter(); } // 设置登陆成功后的重定向地址,或者说是首页地址 // Handler deciding where to redirect user after successful login @Bean public SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler() { SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler = new SavedRequestAwareAuthenticationSuccessHandler(); successRedirectHandler.setDefaultTargetUrl(spConfig.getSuccessLoginUrl()); return successRedirectHandler; } // Handler deciding where to redirect user after failed login @Bean public SimpleUrlAuthenticationFailureHandler authenticationFailureHandler() { SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler(); failureHandler.setUseForward(true); failureHandler.setDefaultFailureUrl(spConfig.getFailLoginUrl()); return failureHandler; } @Bean public SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter() throws Exception { SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter = new SAMLWebSSOHoKProcessingFilter(); samlWebSSOHoKProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler()); samlWebSSOHoKProcessingFilter.setAuthenticationManager(authenticationManager()); samlWebSSOHoKProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler()); return samlWebSSOHoKProcessingFilter; } // Processing filter for WebSSO profile messages @Bean public SAMLProcessingFilter samlWebSSOProcessingFilter() throws Exception { SAMLProcessingFilter samlWebSSOProcessingFilter = new SAMLProcessingFilter(); samlWebSSOProcessingFilter.setAuthenticationManager(authenticationManager()); samlWebSSOProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler()); samlWebSSOProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler()); return samlWebSSOProcessingFilter; } @Bean public metadataGeneratorFilter metadataGeneratorFilter() { return new metadataGeneratorFilter(metadataGenerator()); } // Handler for successful logout @Bean public SimpleUrlLogoutSuccessHandler successLogoutHandler() { SimpleUrlLogoutSuccessHandler successLogoutHandler = new SimpleUrlLogoutSuccessHandler(); successLogoutHandler.setDefaultTargetUrl(spConfig.getSuccessLogoutUrl()); return successLogoutHandler; } // Logout handler terminating local session @Bean public SecurityContextLogoutHandler logoutHandler() { SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler(); logoutHandler.setInvalidateHttpSession(true); logoutHandler.setClearAuthentication(true); return logoutHandler; } // Filter processing incoming logout messages // First argument determines URL user will be redirected to after successful // global logout @Bean public SAMLLogoutProcessingFilter samlLogoutProcessingFilter() { return new SAMLLogoutProcessingFilter(successLogoutHandler(), logoutHandler()); } // Overrides default logout processing filter with the one processing SAML // messages @Bean public SAMLLogoutFilter samlLogoutFilter() { return new SAMLLogoutFilter(successLogoutHandler(), new LogoutHandler[] { logoutHandler() }, new LogoutHandler[] { logoutHandler() }); } // Bindings private ArtifactResolutionProfile artifactResolutionProfile() { final ArtifactResolutionProfileImpl artifactResolutionProfile = new ArtifactResolutionProfileImpl(httpClient()); artifactResolutionProfile.setProcessor(new SAMLProcessorImpl(soapBinding())); return artifactResolutionProfile; } @Bean public HTTPArtifactBinding artifactBinding(ParserPool parserPool, VelocityEngine velocityEngine) { return new HTTPArtifactBinding(parserPool, velocityEngine, artifactResolutionProfile()); } @Bean public HTTPSOAP11Binding soapBinding() { return new HTTPSOAP11Binding(parserPool()); } @Bean public HTTPPostBinding httpPostBinding() { return new HTTPPostBinding(parserPool(), velocityEngine()); } @Bean public HTTPRedirectDeflateBinding httpRedirectDeflateBinding() { return new HTTPRedirectDeflateBinding(parserPool()); } @Bean public HTTPSOAP11Binding httpSOAP11Binding() { return new HTTPSOAP11Binding(parserPool()); } @Bean public HTTPPAOS11Binding httpPAOS11Binding() { return new HTTPPAOS11Binding(parserPool()); } // Processor@Beanpublic SAMLProcessorImpl processor() {Collection bindings = new ArrayList();bindings.add(httpRedirectDeflateBinding());bindings.add(httpPostBinding());bindings.add(artifactBinding(parserPool(), velocityEngine()));bindings.add(httpSOAP11Binding());bindings.add(httpPAOS11Binding());return new SAMLProcessorImpl(bindings);} @Bean public FilterChainProxy samlFilter() throws Exception { List chains = new ArrayList(); chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity http) throws Exception { http .httpBasic() .authenticationEntryPoint(samlEntryPoint()); http .addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class) .addFilterAfter(samlFilter(), BasicAuthenticationFilter.class) .addFilterBefore(samlFilter(), CsrfFilter.class); http .authorizeRequests() .antMatchers("/").permitAll() .antMatchers("/saml @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .authenticationProvider(samlAuthenticationProvider()); } @Override public void afterPropertiesSet() throws Exception { init(); } @Override public void destroy() throws Exception { shutdown(); }}

生成密钥库jks文件

SAML客户端在发送SAML请求时需要进行加密和签名,这就需要密钥,上面配置文件中也有需要去配置jks。jks即密钥库(Java Key Store),里面包含多个公钥和私钥,也可以将认证中心的公钥放入其中,进行解密和验签。这里介绍如何使用jdk的keytool工具生成私钥和自签名证书。

生成密钥库,密钥库包含了公钥和私钥

keytool -genkeypair -alias qianxing -keyalg RSA -keystore samlKeystore.jks

生成公钥,IDP解密时需要使用

keytool -alias qianxing -exportcert -keystore samlKeystore.jks -file public.cer

根据jks生成私钥

keytool -v -importkeystore -srckeystore samlKeystore.jks -srcstoretype jks -destkeystore qianxing.pfx -deststoretype pkcs12

openssl pkcs12 -in qianxing.pfx -nocerts -nodes -out private.key

在idp中注册应用

在之前的文章中的idp工程中classpath下创建一个services目录

新建一个json文件,文件名格式为SAML-XXXXXXXX(唯一标识).json

{ "@class": "org.apereo.cas.support.saml.services.SamlRegisteredService", # sp的entityId "serviceId": "com:ustcinfo:qianxing", # 服务名称 "name": "SAMLService", # 唯一标识id "id": 10000004, "evaluationOrder": 10, # sp元数据位置 "metadataLocation": "http://localhost:8100/spring_security_saml2_sample_war/saml/metadata", # sp公钥位置,如果不需要加密和签名的话可以先不配置 "metadataSignatureLocation": "http://localhost:8100/spring_security_saml2_sample_war/key/qianxing.cer", # 这是关于加密和签名的配置,暂时先不说,先都设置为false好测试 "signAssertions": false, "signResponses": false, "encryptAssertions": false}

重新部署idp待idp部署成功后,sp客户端打包部署访问sp客户端 sp常用API

获取sp元数据:http://ip:port/cotext-path/saml/metadata

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。