spring-security - Azure AD SAML Authentication with Spring Security 5.3.2 (not SAML Extension)
问题描述
I am attempting to replace CAS with Azure Active Directory SAML authentication (SSO) in a Spring Boot API. My version of Spring Security is 5.3.2. Spring boot is 2.3.0.
Documentation has been hard to find. I think this is explained by 8685. I found 8010 and attempted the workaround mentioned there, but my breakpoints there are not getting hit.
Given the current state of the transition from SAML Extension to Spring Security, should I be using the old SAML Extension? I can reach my "success" endpoint with a JSESSIONID and SAMLReponse, but it's encrypted. Is this something I need to do myself? (If so, how?) The SecurityContext / user details are not getting set. I see AccessDenied stack traces in my logs, but I think that's a symptom of the Anonymous user context.
Relevant code is below. I have application.yml and application.properties files, but all config is annotations-based. If you see anything way off base, please let me know! Any guidance would be appreciated.
Here is my SecurityConfig:
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
RelyingPartyRegistration getSaml2AuthenticationConfiguration() throws Exception {
// remote IDP entity ID
String idpEntityId = "https://sts.windows.net/xxxxxxxxxxxx/";
// remote WebSSO Endpoint - Where to Send AuthNRequests to
String webSsoEndpoint = "https://login.microsoftonline.com/xxxxxxxxxxxx/saml2";
// local registration ID
String registrationId = "xxxxxxxxxxxx";
// local entity ID - autogenerated based on URL
String localEntityIdTemplate = "xxxxxxxxxxxx.local";
// local signing (and decryption key)
Saml2X509Credential signingCredential = getSigningCredential(); //private method not included
// IDP certificate for verification of incoming messages
Saml2X509Credential idpVerificationCertificate = getVerificationCertificate(); //private method not included
String acsUrlTemplate = "https://xxxxxxxxxxxx.local/success"; //REST endpoint, see below
return RelyingPartyRegistration.withRegistrationId(registrationId)
.providerDetails(config -> config.entityId(idpEntityId))
.providerDetails(config -> config.webSsoUrl(webSsoEndpoint)).credentials(c -> c.add(signingCredential))
.credentials(c -> c.add(idpVerificationCertificate)).localEntityIdTemplate(localEntityIdTemplate)
.assertionConsumerServiceUrlTemplate(acsUrlTemplate).build();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// Just a test
OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();
http
.headers()
.frameOptions()
.sameOrigin()
.httpStrictTransportSecurity()
.disable()
.and()
.authorizeRequests()
//... more antMatchers and permitAlls
.antMatchers("/success").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/logout").permitAll()
.antMatchers("/error").permitAll().anyRequest().authenticated().and()
.csrf().disable()
.saml2Login(
saml2 -> {
try {
saml2
.authenticationManager(a -> {
// This code is never reached
Authentication result = provider.authenticate(a);
Saml2Authentication saml2Authentication = (Saml2Authentication) result;
return result;
}).relyingPartyRegistrationRepository(
new InMemoryRelyingPartyRegistrationRepository(getSaml2AuthenticationConfiguration())
)
.loginProcessingUrl("/login/{registrationId}");
} catch (Exception e) {
// It made me put this try/catch here... this isn't getting reached either
e.printStackTrace();
}
});
}
}
And my REST endpoint:
@RestController
public class HelloController {
@RequestMapping(value = "/success", method=RequestMethod.POST)
public String saml2Post(HttpServletRequest request) throws IOException {
String jSessionId = request.getHeader("cookie");
System.out.println(jSessionId);
String samlResponse = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
System.out.println(samlResponse);
return "login success";
}
}
And my gradle dependencies (Gradle 6.5):
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
compile 'org.springframework.security:spring-security-config'
compile 'org.springframework.security:spring-security-saml2-service-provider'
compile 'org.springframework.boot:spring-boot-starter-thymeleaf'
compile 'org.springframework.boot:spring-boot-starter-web'
compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
implementation 'org.springframework.boot:spring-boot-starter-freemarker'
implementation 'org.springframework.boot:spring-boot-starter-integration'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-mail'
compile 'org.springframework.security:spring-security-oauth2-client'
compile 'org.springframework.security:spring-security-oauth2-jose'
implementation 'joda-time:joda-time:2.10.6'
implementation 'com.google.guava:guava:29.0-jre'
implementation 'com.opencsv:opencsv:5.2'
implementation 'org.apache.commons:commons-lang3:3.10'
implementation 'net.minidev:json-smart:2.3'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.microsoft.sqlserver:mssql-jdbc'
runtimeOnly 'org.hsqldb:hsqldb'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
testImplementation 'org.springframework.integration:spring-integration-test'
testImplementation 'org.springframework.security:spring-security-test'
}
解决方案
推荐阅读
- amazon-web-services - ssh 到 EC2 上托管的 docker 容器
- logging - kubernetes:kubectl 没有检索到日志
- vue.js - 在vuejs中,单击元素时如何传递html元素?
- java - 创建一个如果 UNICODE_CASE 关闭则失败但打开时匹配的示例
- ethereum - 以太坊 web3js 方法调用失败
- php - MySQL 无法通过远程 php 脚本访问连接
- java - 杰克逊检查可选字段
- javascript - 如何打开/关闭?(Javascript)
- reactjs - 如何使用 Field redux-form 发送道具
- c# - 具有运行模式和编辑模式