在 Spring Boot 中使用 jwt 令牌的具有 http 安全性的 CrossOrigin

Posted

技术标签:

【中文标题】在 Spring Boot 中使用 jwt 令牌的具有 http 安全性的 CrossOrigin【英文标题】:CrossOrigin with http security using jwt token in Spring boot 【发布时间】:2020-08-20 09:45:48 【问题描述】:

我的 Spring Boot 应用程序中存在一些关于跨域和 http 安全性的问题。 当我在控制器类中使用 @crossorigin 注释方法时,我想使用 http 安全性。 但它没有接缝工作,即使该方法未使用@crosorigin,也始终会触发安全性。

有办法解决吗?

Jwtautoconfig 类:

@ManagementContextConfiguration
@ConditionalOnProperty(name = "af.security.active", havingValue = "true")
@Import(EnvironmentConfig.class, JwkRepository.class, JwtTokenUtil.class, 
JwtAuthenticationProvider.class)
@EnableWebSecurity
@EnableConfigurationProperties(JwtSecurityProperties.class)
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Order(Ordered.HIGHEST_PRECEDENCE)
public class JwtAutoConfig extends WebSecurityConfigurerAdapter 


@Value("$af.security.jwt.white-list")
private  String[] ignoredPaths;

@Value("$af.security.job-seeker-role:arbetssökande")
private String jobSeekerRole;

@Value("$af.security.officer-role:handläggare")
private String officer;

@Bean(name = "jwtauthenticationentrypoint")
public JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint() 

    return new JwtAuthenticationEntryPoint();


@Bean
public JwtSecurityHelper securityHelper()
    return new JwtSecurityHelper(jobSeekerRole, officer);




@Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception 
    JwtAuthenticationTokenFilter authenticationTokenFilter = new JwtAuthenticationTokenFilter();
    authenticationTokenFilter.setAuthenticationManager(authenticationManager());
    authenticationTokenFilter.setAuthenticationSuccessHandler(new JwtAuthenticationSuccessHandler());
    return authenticationTokenFilter;


@Override
public void configure(HttpSecurity http) throws Exception 
    http.requestMatchers()
            .and()
            .authorizeRequests()
            .antMatchers("/**")
            .authenticated()
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .csrf()
            .disable();

    // Custom JWT based security filter
    http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);

    // disable page caching
    http.headers().cacheControl();


@Override
public void configure(WebSecurity web) 
    final String[] trimmedIgnoredPaths = Stream.of(ignoredPaths)
            .map(String::trim)
            .toArray(String[]::new);

    web.ignoring()
            .antMatchers(HttpMethod.OPTIONS,"/**")
            .and()
            .ignoring().antMatchers(trimmedIgnoredPaths);



private Config hazelCastConfig()
    Config config = new Config();
    config.setInstanceName("app-cache")
            .setNetworkConfig(new NetworkConfig()
                    .setJoin(new JoinConfig()
                            .setMulticastConfig(new MulticastConfig()
                                    .setEnabled(false)
                            )
                    )
            )
            .addMapConfig(
                    new MapConfig()
                            .setName("object-cache")
                            .setMaxSizeConfig(new MaxSizeConfig(10, MaxSizeConfig.MaxSizePolicy.FREE_HEAP_SIZE))
                            .setEvictionPolicy(EvictionPolicy.LRU)
                            .setStatisticsEnabled(true)
                            .setTimeToLiveSeconds(14400));
    return config;


@Bean(name="hazelcast")
public HazelcastInstance hazelcastInstance() 

    HazelcastInstance hazelcastInstance = new HazelcastInstanceFactory(hazelCastConfig()).getHazelcastInstance();
    return hazelcastInstance;



CorsConfig 类:

@Configuration
public class CorsConfig 

@Bean
public WebMvcConfigurer corsConfigurer()

    return new WebMvcConfigurer() 
        @Override
        public void addCorsMappings(CorsRegistry registry) 
            registry.addMapping("/**")
                    .allowedOrigins("*")
                    .allowedMethods("POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "DELETE", "GET" )
                    .allowCredentials(true);
        
    ;


这是我的控制器类中的方法:

    @ApiOperation(value = "Hämtar alla frånvaron för en lista med användare")
@PostMapping(path= "/hamta-alla-franvaron", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<ExternalFranvaroDTO>> hamtaAllaFranvaron(
        @ApiParam(value = "Identitet objekt som innehåller en lista av PISA_ID", required = true)
        @Valid @RequestBody IdentitetForm identitet)
    logger.info("MOTTAGET Rest-anrop (/hamta-alla-franvaron) Hamtar alla franvaron");
    List<ExternalFranvaroDTO> externalFranvaroDTOLista = new ArrayList<>();
    List<Franvaro> franvaron = franvaroService.hamtaAllaPagaendeOchNyaFriskskrivnaFranvaron(identitet.getPisaIds());

    if(franvaron.isEmpty()) 
        logger.debug("Inga pågende sjuk/vab anmälan");
        return ResponseEntity.noContent().build();
    
    franvaron.forEach( franvaro -> 
        ExternalFranvaroDTO externalFranvaroDTO = transformeraTillExternalFranvaroDTO(franvaro);
        externalFranvaroDTOLista.add(externalFranvaroDTO);
    );

    return ResponseEntity.ok().body(externalFranvaroDTOLista);

现在我只想在使用 @crossorigin 时使用 http 安全

【问题讨论】:

为什么不直接在spring security中激活cross origin,然后在特定端点上配置spring security呢?还有为什么当spring security内置了一个自定义jwt过滤器时? docs.spring.io/spring-security/site/docs/current/reference/… 我该怎么做?我不确定如何在特定端点上配置 spring 安全性 【参考方案1】:

我看不出你为什么要这样组合它。

您应该改为将安全性应用于特定端点并在 spring security 中配置 cors 过滤器,而不是像您所做的那样全局设置它。

如果您阅读 HttpSecurity 下的 spring 安全文档,您可以使用 antMatcher 并使用 ant syntax 匹配端点

protected void configure(HttpSecurity http) throws Exception 
    http
        .authorizeRequests(authorize -> authorize
        .antMatcher( // Here you can define endpoints using ant matching
            "**/foo/**",
            "**/bar/**"
        )
        .authenticated()
    )

    ... // rest of configuration

你也可以使用 spring security 定义一个CORS filter

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http
            // by default uses a Bean by the name of corsConfigurationSource
            .cors(withDefaults())
            ...
    

    @Bean
    CorsConfigurationSource corsConfigurationSource() 
        final CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    

您甚至可以激活和使用内置的jwt filter 并使用您自己的转换器等自定义过滤器。

protected void configure(HttpSecurity http) 
    http
        .authorizeRequests(authorize -> authorize
            .anyRequest().authenticated()
        )
        .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);


        // or add a custom converter
        .oauth2ResourceServer(oauth2 -> oauth2
            .jwt(jwt -> jwt
                // adding a custom converter here
                .jwtAuthenticationConverter(myConverter())
            )
        );

spring 安全文档真的非常非常好,您应该始终首先使用它作为信息来源。

【讨论】:

以上是关于在 Spring Boot 中使用 jwt 令牌的具有 http 安全性的 CrossOrigin的主要内容,如果未能解决你的问题,请参考以下文章

令牌 jwt 可与 spring boot 一起使用,但 angular 存在错误

在 Spring Boot 中使用 jwt 令牌的具有 http 安全性的 CrossOrigin

在spring boot微服务中设置JWT令牌生成中的两个主题

为 Spring Boot 制作黑名单 JWT 令牌

在 Spring Boot 中成功身份验证后未生成 JWT 令牌

如何在使用 Spring Boot 的 JWT 令牌时禁用同一用户帐户的多个登录