使用 JUnit 5 测试 Micronaut 3.3.4 的身份验证为空

发布于 2025-01-13 21:45:02 字数 4739 浏览 5 评论 0原文

尝试使用 JUnit 测试通过 micronaut 安全性保护控制器,将身份验证设置为 NULL。

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@MicronautTest
public class ProductCreateTest extends TestContainerFixture {
    private BearerAccessRefreshToken bearerAccessRefreshToken = null;
    @Inject
    @Client("/")
    HttpClient client;
    private ProductModel productModel;

    @Test
    @Order(1)
    @DisplayName("Should create a JWT token")
    void shouldCreateAJwtToken() {
        UsernamePasswordCredentials creds = new UsernamePasswordCredentials("[email protected]", "RockStar.1");
        HttpRequest request = HttpRequest.POST("/login", creds);
        HttpResponse<BearerAccessRefreshToken> rsp = client.toBlocking().exchange(request, BearerAccessRefreshToken.class);
        bearerAccessRefreshToken = rsp.body();
    }

    @Test
    @Order(2)
    @DisplayName("Should create the product")
    void shouldCreateTheProduct() {
        this.productModel = new ProductModel(
                null,
                Optional.of(true),
                Optional.of(false),
                "Nike shirt",
                "Nike shirt description",
                Optional.of(new CategoryModel(new ObjectId().toString(), "Men", "Men")),
                Optional.of(new SubCategoryModel(new ObjectId().toString(), "Men", "Men")),
                Optional.of(new VendorModel(new ObjectId().toString(), "Men", "Men")),
                Optional.of(List.of(new SelectOptionModel("Shirt", "Shirt"))),
                Optional.of(false)
        );

        HttpRequest request = HttpRequest.POST("/product", this.productModel)
                .header("Authorization",bearerAccessRefreshToken.getAccessToken());
        HttpResponse<ProductModel> rsp = client.toBlocking().exchange(request, ProductModel.class);
        var item = rsp.body();
    }
}

身份验证提供程序

@Singleton
@Requires(env = Environment.TEST)
public record AuthenticationProviderUserPasswordFixture() implements AuthenticationProvider {
    @Override
    public Publisher<AuthenticationResponse> authenticate(HttpRequest<?> httpRequest, AuthenticationRequest<?, ?> authenticationRequest) {
        return Flux.create(emitter -> {
            if (authenticationRequest.getIdentity().equals("[email protected]") && authenticationRequest.getSecret().equals("RockStar.1")) {
                emitter.next(AuthenticationResponse.success((String) authenticationRequest.getIdentity(), List.of("roles1", "roles2")));
                emitter.complete();
            } else {
                emitter.error(AuthenticationResponse.exception());
            }
        }, FluxSink.OverflowStrategy.ERROR);
    }
}

控制器定义

@Post
@IRequirement(resourceName = ClaimType.TAG_PRODUCT, permission = {ClaimValue.TAG_OWNER,ClaimValue.TAG_CREATOR,ClaimValue.TAG_MAINTAINER })
Mono<MutableHttpResponse<?>> post(@Body @Valid ProductModel model);

安全规则

@Singleton
public class AuthorityHandler implements SecurityRule {
    @Override
    public Publisher<SecurityRuleResult> check(HttpRequest<?> request, @Nullable RouteMatch<?> routeMatch, @Nullable Authentication authentication) {
        if (routeMatch instanceof MethodBasedRouteMatch methodBasedRouteMatch) {
            if (methodBasedRouteMatch.hasAnnotation(IRequirement.class)) {
                AnnotationValue<IRequirement> requiredPermissionAnnotation = methodBasedRouteMatch.getAnnotation(IRequirement.class);
                Optional<String> resourceIdName = requiredPermissionAnnotation.stringValue( "resourceName");
                String[] permissions = requiredPermissionAnnotation.stringValues("permission");
                if (permissions.length > 0 && resourceIdName.isPresent() && authentication != null) {
                    Map<String, Object> identityClaims = authentication.getAttributes();
                    if (Arrays.stream(permissions).anyMatch(element ->  identityClaims.containsValue(element)))
                        return Mono.just(SecurityRuleResult.ALLOWED);
                    else
                        return Mono.just(SecurityRuleResult.REJECTED);
                }
            }
        }
        return Mono.just(SecurityRuleResult.UNKNOWN);
    }
}

安全规则中的检查方法 身份验证身份验证为空。但是,从身份验证提供者处,我已成功进行身份验证。

Trying to protect the controller with micronaut security using JUnit testing, getting authentication as NULL.

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@MicronautTest
public class ProductCreateTest extends TestContainerFixture {
    private BearerAccessRefreshToken bearerAccessRefreshToken = null;
    @Inject
    @Client("/")
    HttpClient client;
    private ProductModel productModel;

    @Test
    @Order(1)
    @DisplayName("Should create a JWT token")
    void shouldCreateAJwtToken() {
        UsernamePasswordCredentials creds = new UsernamePasswordCredentials("[email protected]", "RockStar.1");
        HttpRequest request = HttpRequest.POST("/login", creds);
        HttpResponse<BearerAccessRefreshToken> rsp = client.toBlocking().exchange(request, BearerAccessRefreshToken.class);
        bearerAccessRefreshToken = rsp.body();
    }

    @Test
    @Order(2)
    @DisplayName("Should create the product")
    void shouldCreateTheProduct() {
        this.productModel = new ProductModel(
                null,
                Optional.of(true),
                Optional.of(false),
                "Nike shirt",
                "Nike shirt description",
                Optional.of(new CategoryModel(new ObjectId().toString(), "Men", "Men")),
                Optional.of(new SubCategoryModel(new ObjectId().toString(), "Men", "Men")),
                Optional.of(new VendorModel(new ObjectId().toString(), "Men", "Men")),
                Optional.of(List.of(new SelectOptionModel("Shirt", "Shirt"))),
                Optional.of(false)
        );

        HttpRequest request = HttpRequest.POST("/product", this.productModel)
                .header("Authorization",bearerAccessRefreshToken.getAccessToken());
        HttpResponse<ProductModel> rsp = client.toBlocking().exchange(request, ProductModel.class);
        var item = rsp.body();
    }
}

Authentication Provider

@Singleton
@Requires(env = Environment.TEST)
public record AuthenticationProviderUserPasswordFixture() implements AuthenticationProvider {
    @Override
    public Publisher<AuthenticationResponse> authenticate(HttpRequest<?> httpRequest, AuthenticationRequest<?, ?> authenticationRequest) {
        return Flux.create(emitter -> {
            if (authenticationRequest.getIdentity().equals("[email protected]") && authenticationRequest.getSecret().equals("RockStar.1")) {
                emitter.next(AuthenticationResponse.success((String) authenticationRequest.getIdentity(), List.of("roles1", "roles2")));
                emitter.complete();
            } else {
                emitter.error(AuthenticationResponse.exception());
            }
        }, FluxSink.OverflowStrategy.ERROR);
    }
}

Controller defination

@Post
@IRequirement(resourceName = ClaimType.TAG_PRODUCT, permission = {ClaimValue.TAG_OWNER,ClaimValue.TAG_CREATOR,ClaimValue.TAG_MAINTAINER })
Mono<MutableHttpResponse<?>> post(@Body @Valid ProductModel model);

Security Rule

@Singleton
public class AuthorityHandler implements SecurityRule {
    @Override
    public Publisher<SecurityRuleResult> check(HttpRequest<?> request, @Nullable RouteMatch<?> routeMatch, @Nullable Authentication authentication) {
        if (routeMatch instanceof MethodBasedRouteMatch methodBasedRouteMatch) {
            if (methodBasedRouteMatch.hasAnnotation(IRequirement.class)) {
                AnnotationValue<IRequirement> requiredPermissionAnnotation = methodBasedRouteMatch.getAnnotation(IRequirement.class);
                Optional<String> resourceIdName = requiredPermissionAnnotation.stringValue( "resourceName");
                String[] permissions = requiredPermissionAnnotation.stringValues("permission");
                if (permissions.length > 0 && resourceIdName.isPresent() && authentication != null) {
                    Map<String, Object> identityClaims = authentication.getAttributes();
                    if (Arrays.stream(permissions).anyMatch(element ->  identityClaims.containsValue(element)))
                        return Mono.just(SecurityRuleResult.ALLOWED);
                    else
                        return Mono.just(SecurityRuleResult.REJECTED);
                }
            }
        }
        return Mono.just(SecurityRuleResult.UNKNOWN);
    }
}

In the security rule the check method The Authentication authentication is null. However, from the Authentication Provider, I have successful authentication.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

扬花落满肩 2025-01-20 21:45:02

将安全日志添加到 logback.xml 会显示一条消息,指出我在访问令牌前面缺少承载者

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <withJansi>true</withJansi>
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
        <pattern>%cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n
        </pattern>
    </encoder>
</appender>

<root level="info">
    <appender-ref ref="STDOUT"/>
</root>
<logger name="io.micronaut.http.client" level="TRACE"/>
<logger name="io.micronaut.security" level="TRACE"/>

将承载者添加到标头解决了该问题

 HttpRequest request = HttpRequest.POST("/product", this.productModel)
                .header("Authorization","bearer "+bearerAccessRefreshToken.getAccessToken());
        HttpResponse<ProductModel> rsp = client.toBlocking().exchange(request, ProductModel.class);
        var item = rsp.body();

Adding security logs to the logback.xml gives me a message that I was missing bearer in front of access token

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <withJansi>true</withJansi>
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
        <pattern>%cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n
        </pattern>
    </encoder>
</appender>

<root level="info">
    <appender-ref ref="STDOUT"/>
</root>
<logger name="io.micronaut.http.client" level="TRACE"/>
<logger name="io.micronaut.security" level="TRACE"/>

Adding bearer to the header solve the issue

 HttpRequest request = HttpRequest.POST("/product", this.productModel)
                .header("Authorization","bearer "+bearerAccessRefreshToken.getAccessToken());
        HttpResponse<ProductModel> rsp = client.toBlocking().exchange(request, ProductModel.class);
        var item = rsp.body();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文