目前我正在体验新的 Spring 反应式堆栈,并希望在 Spring Session 2.0 中使用反应式功能。
在传统的 Servlet 方法中,Spring Session 提供了一个HttpSessionStrategy来检测 cookie 或请求标头中的会话。它很容易用于为 RESTful APIHeaderHttpSessionStrategy实现类似身份验证的令牌(默认名称为X-AUTH-TOKEN)。
Spring 5核心提供了一个WebSessionIdResolver为Reactive环境做同样的事情。
但是当它与 Spring Security 一起使用并希望它以传统方式工作时,我无法让它工作。
会话配置文件。
@EnableSpringWebSession
public class SessionConfig {
@Bean
public ReactorSessionRepository sessionRepository() {
return new MapReactorSessionRepository(new ConcurrentHashMap<>());
}
@Bean
public WebSessionIdResolver headerWebSessionIdResolver() {
HeaderWebSessionIdResolver resolver = new HeaderWebSessionIdResolver();
resolver.setHeaderName("X-SESSION-ID");
return resolver;
}
}
Run Code Online (Sandbox Code Playgroud)
部分SecurityConfig。
@EnableWebFluxSecurity
class SecurityConfig {
@Bean
SecurityWebFilterChain springWebFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeExchange()
.pathMatchers(HttpMethod.GET, "/posts/**").permitAll()
.pathMatchers(HttpMethod.DELETE, "/posts/**").hasRole("ADMIN") …Run Code Online (Sandbox Code Playgroud) spring spring-security reactive-programming spring-session spring-webflux
在这篇文章的帮助下,我通过 custom 在 Spring 5 WebFlux 应用程序中获得了部分异常处理工作WebExceptionHandler,但是当我想将友好消息中的现有异常转换为客户端时,它不起作用。
我的自定义 WebExceptionHandler 如下所示,完整代码在这里。
WebExchangeBindException cvex = (WebExchangeBindException) ex;
Map<String, String> errors = new HashMap<>();
log.debug("errors:" + cvex.getFieldErrors());
cvex.getFieldErrors().forEach(ev -> errors.put(ev.getField(), ev.getDefaultMessage()));
log.debug("handled errors::" + errors);
try {
DataBuffer db = new DefaultDataBufferFactory().wrap(objectMapper.writeValueAsBytes(errors));
exchange.getResponse().setStatusCode(HttpStatus.UNPROCESSABLE_ENTITY);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
exchange.getResponse().writeWith(Mono.just(db));
return exchange.getResponse().setComplete();
} catch (JsonProcessingException e) {
e.printStackTrace();
return Mono.empty();
}
Run Code Online (Sandbox Code Playgroud)
状态码设置正确,但响应内容长度为 0。
Spring 5.2 带来了 Kotlin 协程支持,Spring ReactWebClient在 Kotlin 扩展中获得了协程支持。
我已经创建了将 GET 作为 Flow 公开的后端服务,请在此处/posts检查代码。
@GetMapping("")\nfun findAll(): Flow<Post> =\n postRepository.findAll()\nRun Code Online (Sandbox Code Playgroud)\n\n在客户端示例中,我尝试通过以下方式使用 WebClient 来使用此 api。
\n\n@GetMapping("")\nsuspend fun findAll(): Flow<Post> =\n client.get()\n .uri("/posts")\n .accept(MediaType.APPLICATION_JSON)\n .awaitExchange()\n .awaitBody()\nRun Code Online (Sandbox Code Playgroud)\n\n由于 Flow 类型的 Jackson 序列化,它失败了。
\n\n由于上面表达式中的awaitXXX方法,我必须使用修饰符suspend来实现这个乐趣。
但是,如果我将正文类型更改为 Any,则以下内容有效,请检查完整代码。
\n\nGetMapping("")\nsuspend fun findAll() =\n client.get()\n .uri("/posts")\n .accept(MediaType.APPLICATION_JSON)\n .awaitExchange()\n .awaitBody<Any>()\nRun Code Online (Sandbox Code Playgroud)\n\n阅读spring ref doc 的Kotlin Coroutines后,Flux 应转换为 Kotlin coroutines Flow。如何处理流的返回类型并suspend …
RSocket 提供了 4 种交互模型。
Spring(和 Spring Boot)提供了 RSocket 集成,使用现有的消息传递基础设施很容易构建一个 RSocket 服务器来隐藏原始的 RSocket API。
@MessageMapping("hello")
public Mono<Void> hello(Greeting p) {
log.info("received: {} at {}", p, Instant.now());
return Mono.empty();
}
@MessageMapping("greet.{name}")
public Mono<String> greet(@DestinationVariable String name, @Payload Greeting p) {
log.info("received: {}, {} at {}", name, p, Instant.now());
return Mono.just("Hello " + name + ", " + p.getMessage() + " at " + Instant.now());
}
@MessageMapping("greet-stream")
public Flux<String> greetStream(@Payload Greeting p) {
log.info("received: {} at {}", p, …Run Code Online (Sandbox Code Playgroud) 我尝试在 RDBMS 中创建用户/角色关系,并希望使用 R2dbc(Spring Data R2dbc) 与后端数据库握手。
假设有三个表,users、roles 和user_roles。
@Table("users")
class User {
@Id
private String username;
private String password;
private String email;
@Builder.Default
private boolean active = true;
@Builder.Default
private List<String> roles = new ArrayList<>();
@Column("created_at")
private LocalDateTime createdDate;
}
Run Code Online (Sandbox Code Playgroud)
与 JPA 不同,R2dbc 重用 spring-data-relational-common(在 Spring Data Jdbc 中也使用)来注释表,但没有解决关系的工具,例如roles此处。
postgresql spring-boot spring-data-jdbc spring-data-r2dbc r2dbc
再次阅读 JUnit 文档后,我发现使用@Nested内部类对测试进行分组并最终在测试报告中以树形结构显示它们是很好的。
但是当我PostController像这样重构时。
@WebFluxTest(
controllers = PostController.class,
excludeAutoConfiguration = {
ReactiveUserDetailsServiceAutoConfiguration.class, ReactiveSecurityAutoConfiguration.class
}
)
@Slf4j
@DisplayName("testing /posts endpoint")
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
public class PostControllerTest {
@Autowired
private WebTestClient client;
@MockBean
private PostRepository posts;
@MockBean
private CommentRepository comments;
@BeforeAll
public static void beforeAll() {
log.debug("before all...");
}
@AfterAll
public static void afterAll() {
log.debug("after all...");
}
@BeforeEach
public void beforeEach() {
log.debug("before each...");
}
@AfterEach
public void afterEach() {
log.debug("after each...");
}
@Nested
@DisplayName("/posts GET")
class GettingAllPosts { …Run Code Online (Sandbox Code Playgroud) I am trying to add more testing codes to improve the quality of my sample codes.
Currently, I have a problem when testing UserRepository (not mock UserRepository), there are some custom methods I added in my custom UserRepository like this.
@EntityRepository(UserEntity)
export class UserRepository extends Repository<UserEntity> {
findByEmail(email: string): Promise<UserEntity> {
return this.findOne({ email: email });
}
}
Run Code Online (Sandbox Code Playgroud)
So I want to verify the findOne is called from the parent Repository.
I tried to add the following …
在传统的Web应用程序中,很容易通过控制器方法来验证请求主体。
ResponseEntity create(@Valid @ResponseBody Post post) {
}
Run Code Online (Sandbox Code Playgroud)
如果它是一个MVC应用程序,我们可以通过注入收集错误BindingResult,并决定是否有来自输入表单的一些验证错误。
在页面中,存在一些Freemarker和Thymeleaf的帮助程序来显示消息。
但是,当我来到Webflux并尝试使用它RouterFunction来定义应用程序中的路由时。例如,
Mono<ServerResponse> create(ServerRequest req) {
return req.bodyToMono(Post.class)
.flatMap { this.posts.save(it) }
.flatMap { ServerResponse.created(URI.create("/posts/".concat(it.getId()))).build() }
}
@Bean
RouterFunction<ServerResponse> routes(PostHandler postController) {
return route(GET("/posts"), postController.&all)
.andRoute(POST("/posts"), postController.&create)
.andRoute(GET("/posts/{id}"), postController.&get)
.andRoute(PUT("/posts/{id}"), postController.&update)
.andRoute(DELETE("/posts/{id}"), postController.&delete)
}
Run Code Online (Sandbox Code Playgroud)
一种可能的方法是将请求数据(Mono或Flux)转换为阻止并注入Validator并手动验证它们。
但是我认为代码看起来有些丑陋。
如何优雅地处理请求正文或表单数据的验证?
是否有更好的方法来验证请求正文或表单数据,并且不会丢失WEB(呈现视图)和REST应用程序的功能和响应功能?
spring functional-programming bean-validation project-reactor spring-webflux
Spring Security 5提供了从Reactive context中ReactiveSecurityContextHolder获取的方法SecurityContext,但是当我想自动实现AuditorAware并获取试听工作时,它不起作用。目前我找不到 的Reactive变体AuditorAware。
@Bean
public AuditorAware<Username> auditor() {
return () -> ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.log()
.filter(a -> a != null && a.isAuthenticated())
.map(Authentication::getPrincipal)
.cast(UserDetails.class)
.map(auth -> new Username(auth.getName()))
.switchIfEmpty(Mono.empty())
.blockOptional();
}
Run Code Online (Sandbox Code Playgroud)
我已经添加了@EnableMongoAuduting我的引导Application类。
关于 Mongo 文档类。我添加了试镜相关的注释。
@CreatedDate
private LocalDateTime createdDate;
@CreatedBy
private Username author;
Run Code Online (Sandbox Code Playgroud)
当我添加帖子时,createdDate已填充,但作者为空。
{"id":"5a49ccdb9222971f40a4ada1","title":"my first post","content":"content of my first post","createdDate":"2018-01-01T13:53:31.234","author":null}
Run Code Online (Sandbox Code Playgroud)
完整的代码在这里,基于Spring Boot 2.0.0.M7。
更新: Spring Boot 2.4.0-M2/Spring Data …
spring spring-security spring-data-mongodb project-reactor spring-webflux
根据Spring Boot Docs,@TestConfiguration测试应自动检测嵌套。
但是在我的测试代码中,当我运行整个测试类时它是有问题的,即使我通过@Import. 测试代码结构如下:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
//@Import(IntegrationTests.TestSecurityConfig.class)
public class IntegrationTests {
// test methods
// test configuration
@TestConfiguration
static class TestSecurityConfig {}
}
Run Code Online (Sandbox Code Playgroud)
当我单独运行单个测试用例(测试方法)时,所有测试都按预期通过,但是当我直接运行测试类时,有些测试失败了,@TestConfiguration没有应用于测试。
这样做的完整代码IntegrationTests是在这里。
更新:在我的代码中添加了一种解决方法以使测试通过。
@TestComponent
@Slf4j
static class TestUserDetailsService implements UserDetailsService {
private final PasswordEncoder passwordEncoder;
TestUserDetailsService(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails user = User.withUsername("user")
.password(passwordEncoder.encode("password"))
.roles("USER")
.accountExpired(false)
.accountLocked(false)
.credentialsExpired(false)
.disabled(false) …Run Code Online (Sandbox Code Playgroud) spring spring-test spring-test-mvc spring-boot spring-boot-test
spring ×7
spring-boot ×5
java ×1
junit5 ×1
kotlin ×1
mockito ×1
nestjs ×1
postgresql ×1
r2dbc ×1
rsocket ×1
spring-test ×1
typeorm ×1