我正在关注tutorial关于 Spring 响应式(Reactive)上下文 (webflux) 中的 JWT。
token 生成工作正常,但是当我将Authorization
与bearer
一起使用时,授权不起作用
这是我所做的:
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class WebSecurityConfig{
@Autowired private JWTReactiveAuthenticationManager authenticationManager;
@Autowired private SecurityContextRepository securityContext;
@Bean public SecurityWebFilterChain configure(ServerHttpSecurity http){
return http.exceptionHandling()
.authenticationEntryPoint((swe , e) -> {
return Mono.fromRunnable(()->{
System.out.println( "authenticationEntryPoint user trying to access unauthorized api end points : "+
swe.getRequest().getRemoteAddress()+
" in "+swe.getRequest().getPath());
swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
});
}).accessDeniedHandler((swe, e) -> {
return Mono.fromRunnable(()->{
System.out.println( "accessDeniedHandler user trying to access unauthorized api end points : "+
swe.getPrincipal().block().getName()+
" in "+swe.getRequest().getPath());
swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
});
})
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(authenticationManager)
.securityContextRepository(securityContext)
.authorizeExchange()
.pathMatchers(HttpMethod.OPTIONS).permitAll()
.pathMatchers("/auth/login").permitAll()
.anyExchange().authenticated()
.and()
.build();
}
如您所见,我只想拒绝除登录或基于选项之外的所有未经授权的请求。
登录工作正常,我得到了一个 token 。
但是尝试注销(我自己实现的一个调整,使其成为全状态,因为我只是在学习)是行不通的。
这是我的注销 Controller :
@RestController
@RequestMapping(AuthController.AUTH)
public class AuthController {
static final String AUTH = "/auth";
@Autowired
private AuthenticationService authService;
@PostMapping("/login")
public Mono<ResponseEntity<?>> login(@RequestBody AuthRequestParam arp) {
String username = arp.getUsername();
String password = arp.getPassword();
return authService.authenticate(username, password);
}
@PostMapping("/logout")
public Mono<ResponseEntity<?>> logout(@RequestBody LogoutRequestParam lrp) {
String token = lrp.getToken();
return authService.logout(token);
}
}
注销请求如下:
如上图所示,我相信我做得很好,但是我收到了错误日志消息:
authenticationEntryPoint user trying to access unauthorized api end points : /127.0.0.1:45776 in /auth/logout
这是我的安全上下文内容:
/**
* we use this class to handle the bearer token extraction
* and pass it to the JWTReactiveAuthentication manager so in the end
* we produce
*
* simply said we extract the authorization we authenticate and
* depending on our implementation we produce a security context
*/
@Component
public class SecurityContextRepository implements ServerSecurityContextRepository {
@Autowired
private JWTReactiveAuthenticationManager authenticationManager;
@Override
public Mono<SecurityContext> load(ServerWebExchange swe) {
ServerHttpRequest request = swe.getRequest();
String authorizationHeaderContent = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
if( authorizationHeaderContent !=null && !authorizationHeaderContent.isEmpty() && authorizationHeaderContent.startsWith("Bearer ")){
String token = authorizationHeaderContent.substring(7);
Authentication authentication = new UsernamePasswordAuthenticationToken(token, token);
return this.authenticationManager.authenticate(authentication).map((auth) -> {
return new SecurityContextImpl(auth);
});
}
return Mono.empty();
}
@Override
public Mono<Void> save(ServerWebExchange arg0, SecurityContext arg1) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
我无法看到或发现我犯的任何问题或错误。哪里错了?
最佳答案
写法有区别
//Wrong
Jwts.builder()
.setSubject(username)
.setClaims(claims)
和
//Correct
Jwts.builder()
.setClaims(claims)
.setSubject(username)
确实,请查看 DefaultJwtBuilder
类中的 setSubject
方法:
@Override
public JwtBuilder setSubject(String sub) {
if (Strings.hasText(sub)) {
ensureClaims().setSubject(sub);
} else {
if (this.claims != null) {
claims.setSubject(sub);
}
}
return this;
}
当首先调用 setSubject(username)
时,ensureClaims()
会创建一个没有您的 DefaultClaims
,并且如果您调用 setClaims(声明)
先前的主题丢失了!这个 JWT 构建器是假的。
否则,您将在 JWTReactiveAuthenticationManager
中导入错误的 Role 类,您必须替换:
import org.springframework.context.support.BeanDefinitionDsl.Role;
由
import com.bridjitlearning.www.jwt.tutorial.domain.Role;
最后,由于 check(token)
,validateToken()
将始终返回 false
。 put
调用来得太晚了,你必须意识到这一点。您可以删除此检查,或者在调用检查方法之前移动 put
执行。
我不确定您想用 resignTokenMemory
做什么,所以我会让您自己修复它:
public Boolean validateToken(String token) {
return !isTokenExpired(token) && resignTokenMemory.check(token);
}
另一件事,您的 token 有效期只有 28.8 秒,为了测试存在的理由,我建议您expiraiton * 1000
。
关于spring - Webflux JWT 授权无法正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56826021/