Spring Security jwt 验证问题?

前端请求 Authorization 必须是正确并且没过期,或不设置这个值才可以,这是为什么,我 SecurityConfig 中已经配置放行了。

Authorization 不设置这个值都可以,为什么错误不行,怎么设置才可以放行中的链接错误也可以,错误和不设置这个值 @AuthenticationPrincipal Jwt jwt 为空就行了

SecurityConfig.java

@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class SecurityConfig {

    @Value("${jwt.public.key}")
    RSAPublicKey key;

    @Value("${jwt.private.key}")
    RSAPrivateKey priv;

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http
                .httpBasic(AbstractHttpConfigurer::disable)
                .csrf(AbstractHttpConfigurer::disable)
                .logout(AbstractHttpConfigurer::disable)
                .cors(Customizer.withDefaults())
                .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/test/**").permitAll()
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer(oauth2 -> {
                    oauth2.jwt(Customizer.withDefaults());
                });

        return http.build();
    }

    @Bean
    JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withPublicKey(this.key).build();
    }

    @Bean
    JwtEncoder jwtEncoder() {
        JWK jwk = new RSAKey.Builder(this.key).privateKey(this.priv).build();
        JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
        return new NimbusJwtEncoder(jwks);
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

TestController.java

@RestController
@RequestMapping("/test")
public class TestController {

    @PostMapping("/list")
    public Map<String, Object> listCart(@AuthenticationPrincipal Jwt jwt, HttpServletRequest req) {
        Map<String, Object> map = new HashMap<>();

        if (jwt != null) {
            map.put("id", jwt.getId());
        }

        return map;
    }

}

前端请求

$fetch('http://localhost:8080/test/list', {
    method: 'POST',
    headers: {
        Authorization: 'Bearer 1111111111',
    }
})
阅读 515
2 个回答

可以用自定义过滤器,我写个例子给你看一下

项目结构

src
└── main
    └── java
        └── com
            └── yourcompany
                └── yourproject
                    ├── config
                    │   └── SecurityConfig.java
                    ├── filter
                    │   └── CustomJwtAuthenticationFilter.java
                    └── controller
                        └── TestController.java

主要改动

  • 自定义过滤器:
    新增:CustomJwtAuthenticationFilter 过滤器,并在过滤器链中配置 addFilterBefore 方法,将其放在 UsernamePasswordAuthenticationFilter 之前。
  • 配置方式:
    原来:使用 SecurityFilterChain 和 HttpSecurity 的 lambda 表达式配置方式。
    修改后:继承 WebSecurityConfigurerAdapter 并重写 configure 方法。

CustomJwtAuthenticationFilter.java

package com.yourcompany.yourproject.filter;

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
package com.yourcompany.yourproject.filter;

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
package com.yourcompany.yourproject.filter;

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CustomJwtAuthenticationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        try {
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            // 清除安全上下文
            SecurityContextHolder.clearContext();
            filterChain.doFilter(request, response);
        }
    }
}

SecurityConfig.java

package com.yourcompany.yourproject.config;

import com.yourcompany.yourproject.filter.CustomJwtAuthenticationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
package com.yourcompany.yourproject.config;

import com.yourcompany.yourproject.filter.CustomJwtAuthenticationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .httpBasic(httpBasic -> httpBasic.disable())
            .csrf(csrf -> csrf.disable())
            .logout(logout -> logout.disable())
            .cors(cors -> cors.and())
            .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/test/**").permitAll()
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2.jwt())
            .addFilterBefore(new CustomJwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

TestController.java

package com.yourcompany.yourproject.controller;

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
package com.yourcompany.yourproject.controller;

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/test")
public class TestController {

    @PostMapping("/list")
    public Map<String, Object> listCart(@AuthenticationPrincipal Jwt jwt, HttpServletRequest req) {
        Map<String, Object> map = new HashMap<>();

        if (jwt != null) {
            map.put("id", jwt.getId());
        } else {
            map.put("message", "JWT is null or invalid");
        }

        return map;
    }
}

前端请求

// 无 Authorization 头部
fetch('http://localhost:8080/test/list', {
    method: 'POST'
})
.then(response => response.json())
.then(data => console.log('No Authorization:', data))
.catch(error => console.error('Error:', error));

// 正确的 Authorization 头部
fetch('http://localhost:8080/test/list', {
// 无 Authorization 头部
fetch('http://localhost:8080/test/list', {
    method: 'POST'
})
.then(response => response.json())
.then(data => console.log('No Authorization:', data))
.catch(error => console.error('Error:', error));

// 正确的 Authorization 头部
fetch('http://localhost:8080/test/list', {
    method: 'POST',
    headers: {
        Authorization: 'Bearer your_actual_jwt_token_here',
    }
})
.then(response => response.json())
.then(data => console.log('Valid Authorization:', data))
.catch(error => console.error('Error:', error));

// 错误的 Authorization 头部
fetch('http://localhost:8080/test/list', {
    method: 'POST',
    headers: {
        Authorization: 'Bearer invalid_token',
    }
})
.then(response => response.json())
.then(data => console.log('Invalid Authorization:', data))
.catch(error => console.error('Error:', error));

检查日志

在 application.properties 中启用调试日志,查看请求被拒绝的具体原因

logging.level.org.springframework.security=DEBUG

image.png
你非要这么实现,你就改写这个类注入就行了,这里获取token以后,根据url过滤规则判断一下,如果是允许可以匿名访问的,最简单的办法就是直接返回空就行了
实际上他这个处理远比你想要实现的功能正确得多,是前端入参不规范

推荐问题
宣传栏