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

如何创建@AnonymousAllowed端点:通过Access Token实现免登录跳转至Web应用首页

解决方案:通过匿名端点实现移动端到Web应用的免登录跳转

核心思路

直接创建一个匿名可访问的端点,接收移动端传递的Cognito Access Token,通过Spring Security完成Token验证、用户认证绑定,并创建Session,最后重定向到Web应用首页。同时利用Spring Security的OAuth2自动刷新机制处理Token过期问题,无需手动实现刷新逻辑。


步骤1:Spring Security配置(放行端点+整合Cognito)

首先在安全配置类中放行目标端点,并配置Cognito作为OAuth2资源服务器和客户端,支持Token自动刷新:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
    private String cognitoIssuerUri;

    @Value("${spring.security.oauth2.client.registration.cognito.client-id}")
    private String clientId;

    @Value("${spring.security.oauth2.client.registration.cognito.client-secret}")
    private String clientSecret;

    @Value("${spring.security.oauth2.client.provider.cognito.token-uri}")
    private String cognitoTokenUri;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/mobile/login").permitAll() // 匿名允许的端点
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt
                    .jwtAuthenticationConverter(jwtAuthConverter())
                    .issuerUri(cognitoIssuerUri)
                )
            )
            .oauth2Client(oauth2 -> oauth2 // 配置客户端用于Token自动刷新
                .clientRegistrationRepository(clientRegistrationRepo())
                .authorizedClientService(authorizedClientService())
            )
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // 按需创建Session
            );
        return http.build();
    }

    // 转换Cognito的JWT声明为Spring Security权限
    private JwtAuthenticationConverter jwtAuthConverter() {
        JwtGrantedAuthoritiesConverter authoritiesConverter = new JwtGrantedAuthoritiesConverter();
        authoritiesConverter.setAuthorityPrefix("ROLE_");
        authoritiesConverter.setAuthoritiesClaimName("cognito:groups");

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

    @Bean
    public ClientRegistrationRepository clientRegistrationRepo() {
        return new InMemoryClientRegistrationRepository(cognitoClientRegistration());
    }

    private ClientRegistration cognitoClientRegistration() {
        return ClientRegistration.withRegistrationId("cognito")
            .clientId(clientId)
            .clientSecret(clientSecret)
            .tokenUri(cognitoTokenUri)
            .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
            .build();
    }

    @Bean
    public AuthorizedClientService authorizedClientService(ClientRegistrationRepository repo) {
        return new InMemoryOAuth2AuthorizedClientService(repo);
    }
}

步骤2:实现匿名认证端点

创建控制器端点,接收移动端传递的Access Token,完成认证绑定并创建Session:

@RestController
public class MobileLoginController {

    private final JwtDecoder jwtDecoder;
    private final OAuth2AuthorizedClientService authorizedClientService;
    private final ClientRegistrationRepository clientRegistrationRepo;

    public MobileLoginController(JwtDecoder jwtDecoder,
                                 OAuth2AuthorizedClientService authorizedClientService,
                                 ClientRegistrationRepository clientRegistrationRepo) {
        this.jwtDecoder = jwtDecoder;
        this.authorizedClientService = authorizedClientService;
        this.clientRegistrationRepo = clientRegistrationRepo;
    }

    @GetMapping("/mobile/login")
    public void mobileLogin(@RequestParam("access_token") String accessToken,
                            HttpServletRequest request,
                            HttpServletResponse response) throws IOException {
        // 1. 验证Cognito Access Token的有效性(自动校验签名、过期时间等)
        Jwt jwt = jwtDecoder.decode(accessToken);

        // 2. 构建Spring Security认证对象
        JwtAuthenticationToken authentication = new JwtAuthenticationToken(jwt,
            jwtAuthConverter().convert(jwt));

        // 3. 将认证信息存入SecurityContext并绑定到Session
        SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
        securityContext.setAuthentication(authentication);
        HttpSession session = request.getSession(true); // 强制创建Session
        session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, securityContext);

        // 4. 保存授权客户端信息,让Spring Security自动处理Token过期刷新
        ClientRegistration clientRegistration = clientRegistrationRepo.findByRegistrationId("cognito");
        if (clientRegistration != null) {
            OAuth2AccessToken oAuth2Token = OAuth2AccessToken.withToken(accessToken)
                .tokenType(OAuth2AccessToken.TokenType.BEARER)
                .expiresAt(jwt.getExpiresAt())
                .build();

            // 如果移动端能提供Refresh Token,这里可以传入,进一步优化自动刷新逻辑
            OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
                clientRegistration, authentication.getName(), oAuth2Token, null);
            authorizedClientService.saveAuthorizedClient(authorizedClient, authentication);
        }

        // 5. 重定向到Web应用首页
        response.sendRedirect("/");
    }

    // 复用权限转换逻辑
    private JwtAuthenticationConverter jwtAuthConverter() {
        JwtGrantedAuthoritiesConverter authoritiesConverter = new JwtGrantedAuthoritiesConverter();
        authoritiesConverter.setAuthorityPrefix("ROLE_");
        authoritiesConverter.setAuthoritiesClaimName("cognito:groups");

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

关键注意事项

  • Token传递方式:移动端可以通过链接参数传递Access Token(如https://your-web-app.com/mobile/login?access_token=xxx),这种方式最适合跳转场景。
  • Refresh Token优化:如果移动端能同步传递Refresh Token,在创建OAuth2AuthorizedClient时传入,Spring Security会在Access Token过期时自动调用Cognito的Token接口刷新,无需用户重新登录。
  • 安全防护:可以给这个端点添加额外验证(如移动端专属签名、IP白名单),防止Token泄露被滥用。
  • 之前方案的问题:返回jsessionid再手动设置Cookie的方式不可靠,因为Session的上下文绑定依赖当前请求环境,直接在认证端点内完成Session创建和认证绑定才是正确路径。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 09:15:42