首页 RSocket与Spring Security简单整合
文章
取消

RSocket与Spring Security简单整合

创建工程

  • greetings-service

    在 start.spring.io 选择 2.3.0 M2 版本 Spring Boot,依赖项如下

    Lombok

    RSocket

    Spring Security

  • greetings-client

    客户端的依赖项也是

    Lombok

    RSocket

    Spring Security

服务端应用

GreetingsServiceApplication.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// 省略导入
// 一个简单的,基于用户名和密码的“问候”应用
// 简单起见,所有类都写在一个文件里
@SpringBootApplication
public class GreetingsServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(GreetingsServiceApplication.class, args);
    }
}

@Configuration
@EnableRSocketSecurity
class RSocketSecurityConfiguration {

    @Bean
    RSocketMessageHandler messageHandler(RSocketStrategies strategies) {
        RSocketMessageHandler mh = new RSocketMessageHandler();
        mh.getArgumentResolverConfigurer()
            .addCustomResolver(new AuthenticationPrincipalArgumentResolver());
        mh.setRSocketStrategies(strategies);
        return mh;
    }

    // 授权
    @Bean
    PayloadSocketAcceptorInterceptor authorization(RSocketSecurity security) {
        return security
                .authorizePayload(spec -> spec
                        .route("greetings")
                        .authenticated()
                        .anyExchange()
                        .permitAll())
                .simpleAuthentication(Customizer.withDefaults())
                .build();
    }

    // 用户认证 Authentication
    @Bean
    MapReactiveUserDetailsService authentication() {
        UserDetails jlong = User.withDefaultPasswordEncoder().username("jlong")
            .password("pw").roles("USER").build();
        UserDetails rwinch = User.withDefaultPasswordEncoder().username("rwinch")
            .password("pw").roles("ADMIN", "USER").build();
        return new MapReactiveUserDetailsService(jlong, rwinch);
    }
}

// DTO
@Data
@AllArgsConstructor
@NoArgsConstructor
class GreetingResponse {
    private String message;
}

@Controller
class GreetingController {

    @MessageMapping("greetings")
    Flux<GreetingResponse> greet(@AuthenticationPrincipal Mono<UserDetails> user) {
        return user
                .map(UserDetails::getUsername)
                .flatMapMany(GreetingController::greet);
    }

    private static Flux<GreetingResponse> greet(String name) {
        return Flux.fromStream(
                Stream.generate(() -> 
                new GreetingResponse("Hello " + name + " @ " + 
                Instant.now().toString())))
                .delayElements(Duration.ofSeconds(1));
    }
}

服务端的配置文件:application.properties

1
spring.rsocket.server.port=8888

客户端应用

GreetingsClientApplication.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@SpringBootApplication
@Log4j2
public class GreetingsClientApplication {

    @SneakyThrows
    public static void main(String[] args) {
        SpringApplication.run(GreetingsClientApplication.class, args);
        System.in.read(); // 让程序不要结束
    }

    private final MimeType mimeType =
            MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
    private final UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("jlong", "pw");

    // 配置所用编码器
    @Bean
    RSocketStrategiesCustomizer rSocketStrategiesCustomizer() {
        return strategies -> strategies.encoder(new SimpleAuthenticationEncoder());
    }

    // 相当于客户端
    @Bean
    RSocketRequester rSocketRequester(RSocketRequester.Builder builder) {
        return builder
                //.setupMetadata(this.credentials, this.mimeType)
                .connectTcp("localhost", 8888)
                .block();
    }

    // 应用就绪时通过RSocket向localhost:8888/greetings发起请求
    // 并将响应消息通过日志输出到控制台
    @Bean
    ApplicationListener<ApplicationReadyEvent> ready(RSocketRequester greetings) {
        return event -> greetings
                .route("greetings")
                .metadata(this.credentials, this.mimeType)
                .data(Mono.empty())
                .retrieveFlux(GreetingResponse.class)
                .subscribe(gr -> log.info("secured response: " + gr.toString()));
    }
}

// DTO
@Data
@AllArgsConstructor
@NoArgsConstructor
class GreetingResponse {
    private String message;
}

结束

感觉直接看代码也挺好理解的,就不勉强添加太多文字说明了。有什么不明白的,可以看视频讲解

本文由作者按照 CC BY 4.0 进行授权

OCP-1Z0-816 模拟测试1回顾

第11章练习回顾