You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在非Spring Boot的Spring应用中实现多认证流(SAML、OIDC等)

非Spring Boot Spring应用多认证方案实现指导

整体核心思路

基于Spring Security的多认证机制,通过多个SecurityFilterChain分别处理不同认证类型的请求,同时共享用户信息与授权上下文。核心是为SAML、OIDC、表单登录分别配置独立的过滤器链,通过请求路径匹配区分不同认证入口,最终统一用户身份转换为系统内的Authentication对象,支持JWT令牌的生成与校验。


一、集成Azure AD SAML 2.0 SSO

1. 依赖配置

在pom.xml中引入Spring Security SAML相关依赖:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-saml2-service-provider</artifactId>
    <version>5.8.0</version>
</dependency>

2. 元数据与客户端配置

手动配置Azure AD的IDP元数据(可从Azure门户下载),并配置SP(服务提供者)元数据:

@Configuration
public class SamlSecurityConfig {
    @Bean
    public Saml2MetadataFilter saml2MetadataFilter(RelyingPartyRegistrationRepository repos) {
        return new Saml2MetadataFilter(repos, new OpenSamlMetadataResolver());
    }

    @Bean
    public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {
        RelyingPartyRegistration azureAdRegistration = RelyingPartyRegistration.withRegistrationId("azure-ad")
                .entityId("your-sp-entity-id")
                .assertionConsumerServiceLocation("https://your-app.com/login/saml2/sso/azure-ad")
                .idpEntityId("https://login.microsoftonline.com/{tenant-id}/v2.0")
                .idpWebSsoUrl("https://login.microsoftonline.com/{tenant-id}/saml2")
                .idpMetadataLocation("classpath:azure-ad-idp-metadata.xml")
                .build();
        return new InMemoryRelyingPartyRegistrationRepository(azureAdRegistration);
    }
}

3. SAML认证过滤器链

配置独立的过滤器链,匹配SAML相关请求:

@Configuration
public class SecurityFilterChainConfig {
    @Bean
    public SecurityFilterChain samlFilterChain(HttpSecurity http) throws Exception {
        http
                .securityMatcher("/login/saml2/**", "/saml2/**")
                .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
                .saml2Login(saml2 -> saml2
                        .registrationId("azure-ad")
                        .successHandler((request, response, authentication) -> {
                            // 登录成功后转换用户身份,生成JWT或存入会话
                            handleAuthenticationSuccess(authentication, response);
                        }));
        return http.build();
    }
}

二、集成Okta OpenID Connect SSO

1. 依赖配置

引入Spring Security OAuth2 Client依赖:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-client</artifactId>
    <version>5.8.0</version>
</dependency>

2. OIDC客户端配置

手动注册Okta客户端信息:

@Configuration
public class OidcSecurityConfig {
    @Bean
    public ClientRegistrationRepository clientRegistrationRepository() {
        ClientRegistration oktaRegistration = ClientRegistration.withRegistrationId("okta")
                .clientId("your-okta-client-id")
                .clientSecret("your-okta-client-secret")
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .redirectUri("https://your-app.com/login/oauth2/code/okta")
                .scope("openid", "profile", "email")
                .authorizationUri("https://your-okta-domain.com/oauth2/v1/authorize")
                .tokenUri("https://your-okta-domain.com/oauth2/v1/token")
                .userInfoUri("https://your-okta-domain.com/oauth2/v1/userinfo")
                .userNameAttributeName(IdTokenClaimNames.SUB)
                .jwkSetUri("https://your-okta-domain.com/oauth2/v1/keys")
                .clientName("Okta")
                .build();
        return new InMemoryClientRegistrationRepository(oktaRegistration);
    }
}

3. OIDC认证过滤器链

配置独立的过滤器链处理OIDC请求:

@Configuration
public class SecurityFilterChainConfig {
    // (接上SAML的过滤器链)
    @Bean
    public SecurityFilterChain oidcFilterChain(HttpSecurity http) throws Exception {
        http
                .securityMatcher("/login/oauth2/**", "/oauth2/**")
                .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
                .oauth2Login(oauth2 -> oauth2
                        .registrationId("okta")
                        .successHandler((request, response, authentication) -> {
                            handleAuthenticationSuccess(authentication, response);
                        }));
        return http.build();
    }
}

三、表单登录与JWT API授权

1. 表单登录配置

基于数据库用户的表单登录,配置DaoAuthenticationProvider:

@Configuration
public class FormLoginSecurityConfig {
    @Bean
    public AuthenticationProvider formAuthProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(passwordEncoder);
        return provider;
    }
}

2. 表单登录过滤器链

配置表单登录的过滤器链,匹配表单登录请求:

@Configuration
public class SecurityFilterChainConfig {
    // (接SAML、OIDC的过滤器链)
    @Bean
    public SecurityFilterChain formLoginFilterChain(HttpSecurity http, AuthenticationProvider formAuthProvider) throws Exception {
        http
                .securityMatcher("/login", "/logout")
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/login").permitAll()
                        .anyRequest().authenticated())
                .authenticationProvider(formAuthProvider)
                .formLogin(form -> form
                        .loginPage("/login")
                        .successHandler((request, response, authentication) -> {
                            handleAuthenticationSuccess(authentication, response);
                        }))
                .logout(logout -> logout.permitAll());
        return http.build();
    }
}

3. JWT令牌生成与校验

令牌生成(登录成功后)

在handleAuthenticationSuccess方法中生成JWT:

private void handleAuthenticationSuccess(Authentication authentication, HttpServletResponse response) throws IOException {
    // 转换Authentication为系统用户信息
    UserDetails userDetails = (UserDetails) authentication.getPrincipal();
    // 生成JWT令牌(使用JJWT库)
    String jwt = Jwts.builder()
            .setSubject(userDetails.getUsername())
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + 3600000))
            .signWith(SignatureAlgorithm.HS256, "your-jwt-secret")
            .compact();
    // 返回令牌到前端或存入Cookie
    response.addHeader("Authorization", "Bearer " + jwt);
    response.sendRedirect("/dashboard");
}

JWT API授权过滤器链

配置独立的过滤器链保护API接口,校验JWT:

@Configuration
public class SecurityFilterChainConfig {
    // (接其他过滤器链)
    @Bean
    public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
        http
                .securityMatcher("/api/**")
                .authorizeHttpRequests(auth -> auth.anyRequest().hasRole("USER"))
                .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt
                        .decoder(jwtDecoder())
                        .jwtAuthenticationConverter(jwtAuthenticationConverter())));
        return http.build();
    }

    @Bean
    public JwtDecoder jwtDecoder() {
        SecretKey secretKey = Keys.hmacShaKeyFor("your-jwt-secret".getBytes());
        return NimbusJwtDecoder.withSecretKey(secretKey).build();
    }

    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");

        JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
        converter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
        return converter;
    }
}

四、关键注意事项

  • 非Spring Boot适配:所有Security相关Bean需手动配置,无自动配置支持,需确保@Configuration类被正确扫描。
  • 用户身份统一:三种认证方式成功后,需将外部身份(SAML断言、OIDC用户信息、数据库用户)转换为统一的UserDetails对象,保证后续授权逻辑一致。
  • 认证路由区分:通过securityMatcher为不同过滤器链指定匹配路径,避免冲突。
  • 会话管理:若无需会话,可配置sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))(API链适用)。

内容的提问来源于stack exchange,提问作者Decypher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 20:54:51