Enable Null checking in spring-security-web via JSpecify

Closes gh-16882
This commit is contained in:
Rob Winch 2025-08-21 14:24:08 -05:00
parent a58f3282d9
commit be64c67af5
No known key found for this signature in database
27 changed files with 237 additions and 36 deletions

View File

@ -1,3 +1,7 @@
plugins {
id 'security-nullability'
}
apply plugin: 'io.spring.convention.spring-module' apply plugin: 'io.spring.convention.spring-module'
dependencies { dependencies {

View File

@ -20,6 +20,7 @@ import java.util.function.Supplier;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
@ -49,12 +50,16 @@ public class DefaultMessageSecurityExpressionHandler<T> extends AbstractSecurity
Message<T> message) { Message<T> message) {
MessageSecurityExpressionRoot root = createSecurityExpressionRoot(authentication, message); MessageSecurityExpressionRoot root = createSecurityExpressionRoot(authentication, message);
StandardEvaluationContext ctx = new StandardEvaluationContext(root); StandardEvaluationContext ctx = new StandardEvaluationContext(root);
ctx.setBeanResolver(getBeanResolver()); BeanResolver beanResolver = getBeanResolver();
if (beanResolver != null) {
// https://github.com/spring-projects/spring-framework/issues/35371
ctx.setBeanResolver(beanResolver);
}
return ctx; return ctx;
} }
@Override @Override
protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, protected SecurityExpressionOperations createSecurityExpressionRoot(@Nullable Authentication authentication,
Message<T> invocation) { Message<T> invocation) {
return createSecurityExpressionRoot(() -> authentication, invocation); return createSecurityExpressionRoot(() -> authentication, invocation);
} }

View File

@ -55,7 +55,7 @@ public final class MessageAuthorizationContextSecurityExpressionHandler
} }
@Override @Override
public EvaluationContext createEvaluationContext(Authentication authentication, public EvaluationContext createEvaluationContext(@Nullable Authentication authentication,
MessageAuthorizationContext<?> message) { MessageAuthorizationContext<?> message) {
return createEvaluationContext(() -> authentication, message); return createEvaluationContext(() -> authentication, message);
} }

View File

@ -18,6 +18,8 @@ package org.springframework.security.messaging.access.expression;
import java.util.Map; import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression; import org.springframework.expression.Expression;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
@ -60,7 +62,7 @@ class MessageExpressionConfigAttribute implements ConfigAttribute, EvaluationCon
} }
@Override @Override
public String getAttribute() { public @Nullable String getAttribute() {
return null; return null;
} }

View File

@ -18,6 +18,8 @@ package org.springframework.security.messaging.access.expression;
import java.util.Collection; import java.util.Collection;
import org.jspecify.annotations.Nullable;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import org.springframework.security.access.AccessDecisionVoter; import org.springframework.security.access.AccessDecisionVoter;
@ -60,7 +62,7 @@ public class MessageExpressionVoter<T> implements AccessDecisionVoter<Message<T>
return ExpressionUtils.evaluateAsBoolean(attr.getAuthorizeExpression(), ctx) ? ACCESS_GRANTED : ACCESS_DENIED; return ExpressionUtils.evaluateAsBoolean(attr.getAuthorizeExpression(), ctx) ? ACCESS_GRANTED : ACCESS_DENIED;
} }
private MessageExpressionConfigAttribute findConfigAttribute(Collection<ConfigAttribute> attributes) { private @Nullable MessageExpressionConfigAttribute findConfigAttribute(Collection<ConfigAttribute> attributes) {
for (ConfigAttribute attribute : attributes) { for (ConfigAttribute attribute : attributes) {
if (attribute instanceof MessageExpressionConfigAttribute) { if (attribute instanceof MessageExpressionConfigAttribute) {
return (MessageExpressionConfigAttribute) attribute; return (MessageExpressionConfigAttribute) attribute;

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Security expression support for {@link org.springframework.messaging.Message}.
*/
@NullMarked
package org.springframework.security.messaging.access.expression;
import org.jspecify.annotations.NullMarked;

View File

@ -20,6 +20,7 @@ import java.util.function.Supplier;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.core.log.LogMessage; import org.springframework.core.log.LogMessage;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
@ -110,7 +111,7 @@ public final class AuthorizationChannelInterceptor implements ChannelInterceptor
@Override @Override
public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object, public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
AuthorizationResult result) { @Nullable AuthorizationResult result) {
} }

View File

@ -16,6 +16,8 @@
package org.springframework.security.messaging.access.intercept; package org.springframework.security.messaging.access.intercept;
import org.jspecify.annotations.Nullable;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.messaging.support.ChannelInterceptor;
@ -83,7 +85,7 @@ public final class ChannelSecurityInterceptor extends AbstractSecurityIntercepto
} }
@Override @Override
public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) { public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, @Nullable Exception ex) {
InterceptorStatusToken token = clearToken(); InterceptorStatusToken token = clearToken();
finallyInvocation(token); finallyInvocation(token);
} }
@ -99,7 +101,7 @@ public final class ChannelSecurityInterceptor extends AbstractSecurityIntercepto
} }
@Override @Override
public void afterReceiveCompletion(Message<?> message, MessageChannel channel, Exception ex) { public void afterReceiveCompletion(@Nullable Message<?> message, MessageChannel channel, @Nullable Exception ex) {
} }
private InterceptorStatusToken clearToken() { private InterceptorStatusToken clearToken() {

View File

@ -17,6 +17,7 @@
package org.springframework.security.messaging.access.intercept; package org.springframework.security.messaging.access.intercept;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -61,7 +62,7 @@ public final class DefaultMessageSecurityMetadataSource implements MessageSecuri
return entry.getValue(); return entry.getValue();
} }
} }
return null; return Collections.emptyList();
} }
@Override @Override

View File

@ -55,7 +55,7 @@ public final class MessageMatcherDelegatingAuthorizationManager implements Autho
} }
@Override @Override
public AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication, public @Nullable AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,
Message<?> message) { Message<?> message) {
if (this.logger.isTraceEnabled()) { if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Authorizing message")); this.logger.trace(LogMessage.format("Authorizing message"));
@ -75,7 +75,8 @@ public final class MessageMatcherDelegatingAuthorizationManager implements Autho
return null; return null;
} }
private MessageAuthorizationContext<?> authorizationContext(MessageMatcher<?> matcher, Message<?> message) { private @Nullable MessageAuthorizationContext<?> authorizationContext(MessageMatcher<?> matcher,
Message<?> message) {
MessageMatcher.MatchResult matchResult = matcher.matcher((Message) message); MessageMatcher.MatchResult matchResult = matcher.matcher((Message) message);
if (!matchResult.isMatch()) { if (!matchResult.isMatch()) {
return null; return null;
@ -179,7 +180,7 @@ public final class MessageMatcherDelegatingAuthorizationManager implements Autho
* @return the {@link Builder.Constraint} that is associated to the * @return the {@link Builder.Constraint} that is associated to the
* {@link MessageMatcher} * {@link MessageMatcher}
*/ */
private Builder.Constraint simpDestMatchers(SimpMessageType type, String... patterns) { private Builder.Constraint simpDestMatchers(@Nullable SimpMessageType type, String... patterns) {
List<MessageMatcher<?>> matchers = new ArrayList<>(patterns.length); List<MessageMatcher<?>> matchers = new ArrayList<>(patterns.length);
for (String pattern : patterns) { for (String pattern : patterns) {
MessageMatcher<Object> matcher = this.messageMatcherBuilder.matcher(type, pattern); MessageMatcher<Object> matcher = this.messageMatcherBuilder.matcher(type, pattern);

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Authorization support for {@link org.springframework.messaging.Message}.
*/
@NullMarked
package org.springframework.security.messaging.access.intercept;
import org.jspecify.annotations.NullMarked;

View File

@ -18,6 +18,8 @@ package org.springframework.security.messaging.context;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import org.jspecify.annotations.Nullable;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations;
@ -110,13 +112,14 @@ public final class AuthenticationPrincipalArgumentResolver implements HandlerMet
} }
@Override @Override
public Object resolveArgument(MethodParameter parameter, Message<?> message) { public @Nullable Object resolveArgument(MethodParameter parameter, Message<?> message) {
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication(); Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
if (authentication == null) { if (authentication == null) {
return null; return null;
} }
Object principal = authentication.getPrincipal(); Object principal = authentication.getPrincipal();
AuthenticationPrincipal authPrincipal = findMethodAnnotation(parameter); AuthenticationPrincipal authPrincipal = findMethodAnnotation(parameter);
Assert.notNull(authPrincipal, "AuthenticationPrincipal must not be null. Run supports first");
String expressionToParse = authPrincipal.expression(); String expressionToParse = authPrincipal.expression();
if (StringUtils.hasLength(expressionToParse)) { if (StringUtils.hasLength(expressionToParse)) {
StandardEvaluationContext context = new StandardEvaluationContext(); StandardEvaluationContext context = new StandardEvaluationContext();
@ -165,7 +168,7 @@ public final class AuthenticationPrincipalArgumentResolver implements HandlerMet
* @param parameter the {@link MethodParameter} to search for an {@link Annotation} * @param parameter the {@link MethodParameter} to search for an {@link Annotation}
* @return the {@link Annotation} that was found or null. * @return the {@link Annotation} that was found or null.
*/ */
private AuthenticationPrincipal findMethodAnnotation(MethodParameter parameter) { private @Nullable AuthenticationPrincipal findMethodAnnotation(MethodParameter parameter) {
if (this.useAnnotationTemplate) { if (this.useAnnotationTemplate) {
return this.scanner.scan(parameter.getParameter()); return this.scanner.scan(parameter.getParameter());
} }

View File

@ -18,6 +18,8 @@ package org.springframework.security.messaging.context;
import java.util.Stack; import java.util.Stack;
import org.jspecify.annotations.Nullable;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessageHandler;
@ -96,7 +98,7 @@ public final class SecurityContextChannelInterceptor implements ExecutorChannelI
} }
@Override @Override
public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) { public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, @Nullable Exception ex) {
cleanup(); cleanup();
} }
@ -107,7 +109,8 @@ public final class SecurityContextChannelInterceptor implements ExecutorChannelI
} }
@Override @Override
public void afterMessageHandled(Message<?> message, MessageChannel channel, MessageHandler handler, Exception ex) { public void afterMessageHandled(Message<?> message, MessageChannel channel, MessageHandler handler,
@Nullable Exception ex) {
cleanup(); cleanup();
} }
@ -131,7 +134,7 @@ public final class SecurityContextChannelInterceptor implements ExecutorChannelI
this.securityContextHolderStrategy.setContext(context); this.securityContextHolderStrategy.setContext(context);
} }
private Authentication getAuthentication(Object user) { private Authentication getAuthentication(@Nullable Object user) {
if ((user instanceof Authentication)) { if ((user instanceof Authentication)) {
return (Authentication) user; return (Authentication) user;
} }

View File

@ -18,6 +18,8 @@ package org.springframework.security.messaging.context;
import java.util.Stack; import java.util.Stack;
import org.jspecify.annotations.Nullable;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessageHandler;
@ -121,7 +123,8 @@ public final class SecurityContextPropagationChannelInterceptor implements Execu
} }
@Override @Override
public void afterMessageHandled(Message<?> message, MessageChannel channel, MessageHandler handler, Exception ex) { public void afterMessageHandled(Message<?> message, MessageChannel channel, MessageHandler handler,
@Nullable Exception ex) {
cleanup(); cleanup();
} }

View File

@ -0,0 +1,24 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Support for establishing the
* {@link org.springframework.security.core.context.SecurityContext} within messaging.
*/
@NullMarked
package org.springframework.security.messaging.context;
import org.jspecify.annotations.NullMarked;

View File

@ -18,6 +18,8 @@ package org.springframework.security.messaging.handler.invocation.reactive;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -108,7 +110,7 @@ public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArg
private boolean useAnnotationTemplate = false; private boolean useAnnotationTemplate = false;
private BeanResolver beanResolver; private @Nullable BeanResolver beanResolver;
private ReactiveAdapterRegistry adapterRegistry = ReactiveAdapterRegistry.getSharedInstance(); private ReactiveAdapterRegistry adapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
@ -149,7 +151,8 @@ public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArg
// @formatter:on // @formatter:on
} }
private Object resolvePrincipal(MethodParameter parameter, Object principal) { @NullUnmarked
private @Nullable Object resolvePrincipal(MethodParameter parameter, @Nullable Object principal) {
AuthenticationPrincipal authPrincipal = findMethodAnnotation(parameter); AuthenticationPrincipal authPrincipal = findMethodAnnotation(parameter);
String expressionToParse = authPrincipal.expression(); String expressionToParse = authPrincipal.expression();
if (StringUtils.hasLength(expressionToParse)) { if (StringUtils.hasLength(expressionToParse)) {
@ -169,7 +172,7 @@ public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArg
return principal; return principal;
} }
private boolean isInvalidType(MethodParameter parameter, Object principal) { private boolean isInvalidType(MethodParameter parameter, @Nullable Object principal) {
if (principal == null) { if (principal == null) {
return false; return false;
} }
@ -206,7 +209,7 @@ public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArg
* @param parameter the {@link MethodParameter} to search for an {@link Annotation} * @param parameter the {@link MethodParameter} to search for an {@link Annotation}
* @return the {@link Annotation} that was found or null. * @return the {@link Annotation} that was found or null.
*/ */
private AuthenticationPrincipal findMethodAnnotation(MethodParameter parameter) { private @Nullable AuthenticationPrincipal findMethodAnnotation(MethodParameter parameter) {
if (this.useAnnotationTemplate) { if (this.useAnnotationTemplate) {
return this.scanner.scan(parameter.getParameter()); return this.scanner.scan(parameter.getParameter());
} }

View File

@ -18,6 +18,7 @@ package org.springframework.security.messaging.handler.invocation.reactive;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import org.jspecify.annotations.Nullable;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -106,7 +107,7 @@ public class CurrentSecurityContextArgumentResolver implements HandlerMethodArgu
private boolean useAnnotationTemplate = false; private boolean useAnnotationTemplate = false;
private BeanResolver beanResolver; private @Nullable BeanResolver beanResolver;
private ReactiveAdapterRegistry adapterRegistry = ReactiveAdapterRegistry.getSharedInstance(); private ReactiveAdapterRegistry adapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
@ -159,7 +160,7 @@ public class CurrentSecurityContextArgumentResolver implements HandlerMethodArgu
// @formatter:on // @formatter:on
} }
private Object resolveSecurityContext(MethodParameter parameter, Object securityContext) { private @Nullable Object resolveSecurityContext(MethodParameter parameter, Object securityContext) {
CurrentSecurityContext contextAnno = findMethodAnnotation(parameter); CurrentSecurityContext contextAnno = findMethodAnnotation(parameter);
if (contextAnno != null) { if (contextAnno != null) {
return resolveSecurityContextFromAnnotation(contextAnno, parameter, securityContext); return resolveSecurityContextFromAnnotation(contextAnno, parameter, securityContext);
@ -167,14 +168,17 @@ public class CurrentSecurityContextArgumentResolver implements HandlerMethodArgu
return securityContext; return securityContext;
} }
private Object resolveSecurityContextFromAnnotation(CurrentSecurityContext contextAnno, MethodParameter parameter, private @Nullable Object resolveSecurityContextFromAnnotation(CurrentSecurityContext contextAnno,
Object securityContext) { MethodParameter parameter, Object securityContext) {
String expressionToParse = contextAnno.expression(); String expressionToParse = contextAnno.expression();
if (StringUtils.hasLength(expressionToParse)) { if (StringUtils.hasLength(expressionToParse)) {
StandardEvaluationContext context = new StandardEvaluationContext(); StandardEvaluationContext context = new StandardEvaluationContext();
context.setRootObject(securityContext); context.setRootObject(securityContext);
context.setVariable("this", securityContext); context.setVariable("this", securityContext);
context.setBeanResolver(this.beanResolver); if (this.beanResolver != null) {
// https://github.com/spring-projects/spring-framework/issues/35371
context.setBeanResolver(this.beanResolver);
}
Expression expression = this.parser.parseExpression(expressionToParse); Expression expression = this.parser.parseExpression(expressionToParse);
securityContext = expression.getValue(context); securityContext = expression.getValue(context);
} }
@ -187,7 +191,7 @@ public class CurrentSecurityContextArgumentResolver implements HandlerMethodArgu
return securityContext; return securityContext;
} }
private boolean isInvalidType(MethodParameter parameter, Object value) { private boolean isInvalidType(MethodParameter parameter, @Nullable Object value) {
if (value == null) { if (value == null) {
return false; return false;
} }
@ -223,7 +227,7 @@ public class CurrentSecurityContextArgumentResolver implements HandlerMethodArgu
* @param parameter the {@link MethodParameter} to search for an {@link Annotation} * @param parameter the {@link MethodParameter} to search for an {@link Annotation}
* @return the {@link Annotation} that was found or null. * @return the {@link Annotation} that was found or null.
*/ */
private CurrentSecurityContext findMethodAnnotation(MethodParameter parameter) { private @Nullable CurrentSecurityContext findMethodAnnotation(MethodParameter parameter) {
if (this.useAnnotationTemplate) { if (this.useAnnotationTemplate) {
return this.scanner.scan(parameter.getParameter()); return this.scanner.scan(parameter.getParameter());
} }

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Reactive support for resolving security related arguments.
*/
@NullMarked
package org.springframework.security.messaging.handler.invocation.reactive;
import org.jspecify.annotations.NullMarked;

View File

@ -107,7 +107,7 @@ public final class PathPatternMessageMatcher implements MessageMatcher<Object> {
return (pathMatchInfo != null) ? MatchResult.match(pathMatchInfo.getUriVariables()) : MatchResult.notMatch(); return (pathMatchInfo != null) ? MatchResult.match(pathMatchInfo.getUriVariables()) : MatchResult.notMatch();
} }
private static String getDestination(Message<?> message) { private static @Nullable String getDestination(Message<?> message) {
return SimpMessageHeaderAccessor.getDestination(message.getHeaders()); return SimpMessageHeaderAccessor.getDestination(message.getHeaders());
} }

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Support for matching messages.
*/
@NullMarked
package org.springframework.security.messaging.util.matcher;
import org.jspecify.annotations.NullMarked;

View File

@ -19,6 +19,8 @@ package org.springframework.security.messaging.web.csrf;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.util.Map; import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
@ -70,7 +72,7 @@ public final class XorCsrfChannelInterceptor implements ChannelInterceptor {
* @param actual * @param actual
* @return * @return
*/ */
private static boolean equalsConstantTime(String expected, String actual) { private static boolean equalsConstantTime(String expected, @Nullable String actual) {
if (expected == actual) { if (expected == actual) {
return true; return true;
} }

View File

@ -18,6 +18,8 @@ package org.springframework.security.messaging.web.csrf;
import java.util.Base64; import java.util.Base64;
import org.jspecify.annotations.Nullable;
import org.springframework.security.crypto.codec.Utf8; import org.springframework.security.crypto.codec.Utf8;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -33,7 +35,7 @@ final class XorCsrfTokenUtils {
private XorCsrfTokenUtils() { private XorCsrfTokenUtils() {
} }
static String getTokenValue(String actualToken, String token) { static @Nullable String getTokenValue(@Nullable String actualToken, String token) {
byte[] actualBytes; byte[] actualBytes;
try { try {
actualBytes = Base64.getUrlDecoder().decode(actualToken); actualBytes = Base64.getUrlDecoder().decode(actualToken);

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Support CSRF protection in messages.
*/
@NullMarked
package org.springframework.security.messaging.web.csrf;
import org.jspecify.annotations.NullMarked;

View File

@ -19,6 +19,7 @@ package org.springframework.security.messaging.web.socket.server;
import java.util.Map; import java.util.Map;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.jspecify.annotations.Nullable;
import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServerHttpResponse;
@ -62,7 +63,7 @@ public final class CsrfTokenHandshakeInterceptor implements HandshakeInterceptor
@Override @Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Exception exception) { @Nullable Exception exception) {
} }
} }

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Reactive Security CSRF protection.
*/
@NullMarked
package org.springframework.security.messaging.web.socket.server;
import org.jspecify.annotations.NullMarked;

View File

@ -74,7 +74,7 @@ public class ExpressionBasedMessageSecurityMetadataSourceFactoryTests {
@Test @Test
public void createExpressionMessageMetadataSourceNoMatch() { public void createExpressionMessageMetadataSourceNoMatch() {
Collection<ConfigAttribute> attrs = this.source.getAttributes(this.message); Collection<ConfigAttribute> attrs = this.source.getAttributes(this.message);
assertThat(attrs).isNull(); assertThat(attrs).isEmpty();
} }
@Test @Test

View File

@ -67,8 +67,8 @@ public class DefaultMessageSecurityMetadataSourceTests {
} }
@Test @Test
public void getAttributesNull() { public void getAttributesEmpty() {
assertThat(this.source.getAttributes(this.message)).isNull(); assertThat(this.source.getAttributes(this.message)).isEmpty();
} }
@Test @Test