spring - Webflux JWT 授权无法正常工作
问题描述
我正在学习关于 Spring 响应式上下文 (webflux) 中的 JWT的教程。
Authorization
令牌生成工作正常,但是当我使用with时授权不起作用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();
}
如您所见,我只想拒绝所有未授权的请求,而不是登录或基于选项的请求。
登录工作正常,我得到了一个令牌。
但是尝试注销(由于我只是在学习,我实现了自己的调整以使其状态完整)是行不通的。
这是我的注销控制器:
@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 用户尝试访问未经授权的 api 端点:/auth/logout 中的 /127.0.0.1:45776
这是我的安全上下文内容:
/**
* 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)
确实,看看类中setSubject
的方法DefaultJwtBuilder
:
@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(claims)
先例,则丢失!这个 JWT 构建器是假的。
否则,您在 中导入了错误的 Role 类JWTReactiveAuthenticationManager
,您必须替换:
import org.springframework.context.support.BeanDefinitionDsl.Role;
经过
import com.bridjitlearning.www.jwt.tutorial.domain.Role;
最后同样validateToken()
重要false
的是,由于check(token)
. put
电话来得太晚了,你必须意识到这一点。要么删除此检查,要么put
在调用检查方法之前移动执行。
我不确定你想做什么resignTokenMemory
,所以我会让你自己修复它:
public Boolean validateToken(String token) {
return !isTokenExpired(token) && resignTokenMemory.check(token);
}
另一件事,您的令牌仅在 28.8 秒内有效,为了测试理由,我建议您这样做expiraiton * 1000
。
推荐阅读
- c++ - 非抽象但无状态的类对于多重继承是否与纯抽象类一样安全?
- javascript - 我可以使用javascript将数据插入数据库吗?
- python - 了解模块和绝对/相对包导入
- abap - 检测报告是否为 SAP 查询
- android - 如何在设计编辑器预览的工具栏中显示返回箭头?
- c# - .NET Core - UniTest 项目的 Nuget 包显示警告“程序集不在 'lib' 文件夹内”
- ios - 从数组中删除匹配的条目
- scala - 如何在scala中将多个TIFF组合成一个大的Geotiff?
- ruby-on-rails - 在 Rails 中加载嵌套资源的正确方法
- python - 绘图中线条颜色的条件格式