英文:
spring security is authenticating permitted requests
问题
我为Spring Security配置了JWT身份验证提供程序。以下是我的SecurityConfig.java
文件。
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
// ... 省略了部分代码 ...
}
如您所见,我已允许与/auth/**
匹配的请求。但是这些端点仍然返回401错误。
这是我的Auth控制器。
@RestController
@RequestMapping("/auth")
public class AuthController {
// ... 省略了部分代码 ...
}
能否有人帮我找出我做错了什么?
英文:
I configured jwt auth provider for spring security. Below is my SecurityConfig.java
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
private final UserDetailsServiceImpl userDetailsService;
private final AuthEntryPointJwt unauthorizedHandler;
public SecurityConfig(UserDetailsServiceImpl userDetailsService, AuthEntryPointJwt unauthorizedHandler) {
this.userDetailsService = userDetailsService;
this.unauthorizedHandler = unauthorizedHandler;
}
@Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.csrf(AbstractHttpConfigurer::disable)
.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth ->
auth.requestMatchers("/auth/**").permitAll()
.requestMatchers("/api/**").authenticated()
.anyRequest().authenticated()
).addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class)
.authenticationProvider(authenticationProvider())
.build();
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(false)
.maxAge(3600);
}
};
}
}
As you can see i've permitted requests matching /auth/**
. But these endpoints are returning 401 anyway.
My auth controller
@RestController
@RequestMapping("/auth")
public class AuthController {
private final AuthenticationManager authenticationManager;
private final UserRepository userRepository;
private final PasswordEncoder encoder;
private final JwtUtils jwtUtils;
public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, PasswordEncoder encoder, JwtUtils jwtUtils) {
this.authenticationManager = authenticationManager;
this.userRepository = userRepository;
this.encoder = encoder;
this.jwtUtils = jwtUtils;
}
@PostMapping("/signin")
public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
ResponseCookie jwtCookie = jwtUtils.generateJwtCookie(userDetails);
return ResponseEntity.ok().header(HttpHeaders.SET_COOKIE, jwtCookie.toString())
.body(new UserInfoResponse(userDetails.getId(),
userDetails.getUsername(),
userDetails.getEmail()));
}
@PostMapping("/signup")
public ResponseEntity<?> registerUser(@Valid @RequestBody SignupRequest signUpRequest) {
if (userRepository.existsByUsername(signUpRequest.getUsername())) {
return ResponseEntity.badRequest().body("Error: Username is already taken!");
}
if (userRepository.existsByEmail(signUpRequest.getEmail())) {
return ResponseEntity.badRequest().body("Error: Email is already in use!");
}
// Create new user's account
User user = new User(signUpRequest.getUsername(),
signUpRequest.getEmail(),
encoder.encode(signUpRequest.getPassword()));
userRepository.save(user);
return ResponseEntity.ok("User registered successfully!");
}
@PostMapping("/signout")
public ResponseEntity<?> logoutUser() {
ResponseCookie cookie = jwtUtils.getCleanJwtCookie();
return ResponseEntity.ok().header(HttpHeaders.SET_COOKIE, cookie.toString())
.body("You've been signed out!");
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class SignupRequest {
private String username;
private String email;
private String password;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class LoginRequest {
private String username;
private String password;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class UserInfoResponse {
private Long id;
private String username;
private String email;
}
Could someone help me figure out what i'm doing wrong?
===== Update =====
Here's my AuthTokenFilter
@Service
public class AuthTokenFilter extends OncePerRequestFilter {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private UserDetailsServiceImpl userDetailsService;
private static final Logger logger = LoggerFactory.getLogger(AuthTokenFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
String username = jwtUtils.getUserNameFromJwtToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails,
null,
userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
logger.error("Cannot set user authentication: {}", e);
}
filterChain.doFilter(request, response);
}
private String parseJwt(HttpServletRequest request) {
return request.getHeader(HttpHeaders.AUTHORIZATION);
}
}
答案1
得分: 3
对于Spring Boot 3.1.1,您可以参考下面的安全性代码:
@EnableWebSecurity
@Configuration
@RequiredArgsConstructor
public class SecurityConfig {
private final UserDetailServiceImpl userDetailsService;
private final BCryptPasswordEncoder encoder;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.authorizeRequests()
.requestMatchers("/category/add").authenticated()
.requestMatchers("/authenticate", "/register").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling(ex -> ex.authenticationEntryPoint(point))
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return new CustomAuthenticationManager();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(encoder);
return authenticationProvider;
}
}
CustomAuthenticationManager:
public class CustomAuthenticationManager implements AuthenticationManager {
@Autowired
private DaoAuthenticationProvider authenticationProvider;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
return authenticationProvider.authenticate(authentication);
}
}
英文:
For springboot 3.1.1 you can refer below code for security :
@EnableWebSecurity
@Configuration
@RequiredArgsConstructor
public class SecurityConfig {
private final UserDetailServiceImpl userDetailsService;
private final BCryptPasswordEncoder encoder;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.authorizeRequests().
requestMatchers("/category/add")
.authenticated()
.requestMatchers("/authenticate","/register").permitAll()
.anyRequest()
.authenticated()
.and().exceptionHandling(ex -> ex.authenticationEntryPoint(point))
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return new CustomAuthenticationManager();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(encoder);
return authenticationProvider;
}
}
CustomAuthentcationManager :
public class CustomAuthenticationManager implements AuthenticationManager {
@Autowired
private DaoAuthenticationProvider authenticationProvider;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
return authenticationProvider.authenticate(authentication);
}
}
答案2
得分: 0
添加了对已验证端点的请求匹配器,并允许任何其他请求通过。
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.csrf(c -> c.disable())
.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth ->
auth
.requestMatchers("/api/**").authenticated()
.requestMatchers("/auth/**").permitAll()
.anyRequest().permitAll()
)
.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class)
.authenticationProvider(authenticationProvider())
.build();
}
这有点奇怪。基本上,在最后的 anyRequest().authenticated()
会覆盖对请求匹配器的 permitAll
。
因此,决定添加对已验证端点的匹配器,并允许其他所有请求通过。
如果您认为这不是最佳解决方案,欢迎分享您的想法和备选方案。
英文:
Add request matcher for authenticated endpoint and permitting any other requests worked.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.csrf(c -> c.disable())
.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth ->
auth
.requestMatchers("/api/**").authenticated()
.requestMatchers("/auth/**").permitAll()
.anyRequest().permitAll()
)
.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class)
.authenticationProvider(authenticationProvider())
.build();
}
This is strange. Basically anyRequest().authenticated()
at the end overrides the permitAll
on request matcher.
So, decided to add matchers for authenticated endpoints and permit everything else.
Feel free to post you thoughts and alternate solutions if you think this is not an optimal solution
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论