32
32
import org .springframework .core .ReactiveAdapter ;
33
33
import org .springframework .core .ReactiveAdapterRegistry ;
34
34
import org .springframework .security .access .prepost .PreAuthorize ;
35
+ import org .springframework .security .authorization .AuthorizationDecision ;
36
+ import org .springframework .security .authorization .AuthorizationDeniedException ;
37
+ import org .springframework .security .authorization .AuthorizationResult ;
35
38
import org .springframework .security .authorization .ReactiveAuthorizationManager ;
36
39
import org .springframework .security .core .Authentication ;
37
40
import org .springframework .util .Assert ;
@@ -57,6 +60,8 @@ public final class AuthorizationManagerBeforeReactiveMethodInterceptor implement
57
60
58
61
private int order = AuthorizationInterceptorsOrder .FIRST .getOrder ();
59
62
63
+ private final AuthorizationDeniedPostProcessor <MethodInvocation > postProcessor = new ReactiveAuthorizationDeniedPostProcessorAdapter ();
64
+
60
65
/**
61
66
* Creates an instance for the {@link PreAuthorize} annotation.
62
67
* @return the {@link AuthorizationManagerBeforeReactiveMethodInterceptor} to use
@@ -112,31 +117,65 @@ public Object invoke(MethodInvocation mi) throws Throwable {
112
117
+ " must return an instance of org.reactivestreams.Publisher "
113
118
+ "(for example, a Mono or Flux) or the function must be a Kotlin coroutine "
114
119
+ "in order to support Reactor Context" );
115
- Mono <Authentication > authentication = ReactiveAuthenticationUtils .getAuthentication ();
116
120
ReactiveAdapter adapter = ReactiveAdapterRegistry .getSharedInstance ().getAdapter (type );
117
- Mono <Void > preAuthorize = this .authorizationManager .verify (authentication , mi );
118
121
if (hasFlowReturnType ) {
119
122
if (isSuspendingFunction ) {
120
- return preAuthorize . thenMany ( Flux .defer (() -> ReactiveMethodInvocationUtils .proceed (mi )));
123
+ return preAuthorized ( mi , Flux .defer (() -> ReactiveMethodInvocationUtils .proceed (mi )));
121
124
}
122
125
else {
123
126
Assert .state (adapter != null , () -> "The returnType " + type + " on " + method
124
127
+ " must have a org.springframework.core.ReactiveAdapter registered" );
125
- Flux <? > response = preAuthorize
126
- . thenMany ( Flux .defer (() -> adapter .toPublisher (ReactiveMethodInvocationUtils .proceed (mi ))));
128
+ Flux <Object > response = preAuthorized ( mi ,
129
+ Flux .defer (() -> adapter .toPublisher (ReactiveMethodInvocationUtils .proceed (mi ))));
127
130
return KotlinDelegate .asFlow (response );
128
131
}
129
132
}
130
133
if (isMultiValue (type , adapter )) {
131
- Publisher <?> publisher = Flux .defer (() -> ReactiveMethodInvocationUtils .proceed (mi ));
132
- Flux <?> result = preAuthorize .thenMany (publisher );
134
+ Flux <?> result = preAuthorized (mi , Flux .defer (() -> ReactiveMethodInvocationUtils .proceed (mi )));
133
135
return (adapter != null ) ? adapter .fromPublisher (result ) : result ;
134
136
}
135
- Mono <?> publisher = Mono .defer (() -> ReactiveMethodInvocationUtils .proceed (mi ));
136
- Mono <?> result = preAuthorize .then (publisher );
137
+ Mono <?> result = preAuthorized (mi , Mono .defer (() -> ReactiveMethodInvocationUtils .proceed (mi )));
137
138
return (adapter != null ) ? adapter .fromPublisher (result ) : result ;
138
139
}
139
140
141
+ private Flux <Object > preAuthorized (MethodInvocation mi , Flux <Object > mapping ) {
142
+ Mono <Authentication > authentication = ReactiveAuthenticationUtils .getAuthentication ();
143
+ return this .authorizationManager .check (authentication , mi )
144
+ .switchIfEmpty (Mono .just (new AuthorizationDecision (false )))
145
+ .flatMapMany ((decision ) -> {
146
+ if (decision .isGranted ()) {
147
+ return mapping ;
148
+ }
149
+ return postProcess (decision , mi );
150
+ });
151
+ }
152
+
153
+ private Mono <Object > preAuthorized (MethodInvocation mi , Mono <Object > mapping ) {
154
+ Mono <Authentication > authentication = ReactiveAuthenticationUtils .getAuthentication ();
155
+ return this .authorizationManager .check (authentication , mi )
156
+ .switchIfEmpty (Mono .just (new AuthorizationDecision (false )))
157
+ .flatMap ((decision ) -> {
158
+ if (decision .isGranted ()) {
159
+ return mapping ;
160
+ }
161
+ return postProcess (decision , mi );
162
+ });
163
+ }
164
+
165
+ private Mono <Object > postProcess (AuthorizationDecision decision , MethodInvocation mi ) {
166
+ return Mono .fromSupplier (() -> {
167
+ if (decision instanceof PostProcessableAuthorizationDecision <?> postProcessableDecision ) {
168
+ return postProcessableDecision .postProcess ();
169
+ }
170
+ return this .postProcessor .postProcessResult (mi , decision );
171
+ }).flatMap ((result ) -> {
172
+ if (Mono .class .isAssignableFrom (result .getClass ())) {
173
+ return (Mono <?>) result ;
174
+ }
175
+ return Mono .justOrEmpty (result );
176
+ });
177
+ }
178
+
140
179
private boolean isMultiValue (Class <?> returnType , ReactiveAdapter adapter ) {
141
180
if (Flux .class .isAssignableFrom (returnType )) {
142
181
return true ;
@@ -179,4 +218,14 @@ private static Object asFlow(Publisher<?> publisher) {
179
218
180
219
}
181
220
221
+ private static class ReactiveAuthorizationDeniedPostProcessorAdapter
222
+ implements AuthorizationDeniedPostProcessor <MethodInvocation > {
223
+
224
+ @ Override
225
+ public Object postProcessResult (MethodInvocation contextObject , AuthorizationResult result ) {
226
+ return Mono .error (new AuthorizationDeniedException ("Access Denied" , result ));
227
+ }
228
+
229
+ }
230
+
182
231
}
0 commit comments