Skip to content

Commit bdca146

Browse files
sbespalovkostya05983
authored andcommitted
issue/6506: AuthenticationConverter implementation
1 parent 236df77 commit bdca146

File tree

9 files changed

+761
-53
lines changed

9 files changed

+761
-53
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.web.authentication;
17+
18+
import javax.servlet.http.HttpServletRequest;
19+
20+
import org.springframework.security.authentication.AuthenticationManager;
21+
import org.springframework.security.core.Authentication;
22+
import org.springframework.security.core.AuthenticationException;
23+
24+
/**
25+
* A strategy used for converting from a {@link HttpServletRequest} to an
26+
* {@link Authentication} of particular type. Used to authenticate with
27+
* appropriate {@link AuthenticationManager}. If the result is null, then it
28+
* signals that no authentication attempt should be made. It is also possible to
29+
* throw {@link AuthenticationException} within the
30+
* {@link #convert(HttpServletRequest)} if there was invalid Authentication
31+
* scheme value.
32+
*
33+
* @author Sergey Bespalov
34+
* @since 5.2.0
35+
*/
36+
public interface AuthenticationConverter {
37+
38+
Authentication convert(HttpServletRequest request);
39+
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.web.authentication;
17+
18+
import java.io.IOException;
19+
20+
import javax.servlet.ServletException;
21+
import javax.servlet.http.HttpServletRequest;
22+
import javax.servlet.http.HttpServletResponse;
23+
24+
import org.springframework.security.core.AuthenticationException;
25+
import org.springframework.security.web.AuthenticationEntryPoint;
26+
import org.springframework.util.Assert;
27+
28+
/**
29+
* Adapts a {@link AuthenticationEntryPoint} into a {@link AuthenticationFailureHandler}
30+
*
31+
* @author sbespalov
32+
* @since 5.2.0
33+
*/
34+
public class AuthenticationEntryPointFailureHandler implements AuthenticationFailureHandler {
35+
36+
private final AuthenticationEntryPoint authenticationEntryPoint;
37+
38+
public AuthenticationEntryPointFailureHandler(AuthenticationEntryPoint authenticationEntryPoint) {
39+
Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint cannot be null");
40+
this.authenticationEntryPoint = authenticationEntryPoint;
41+
}
42+
43+
@Override
44+
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
45+
AuthenticationException exception) throws IOException, ServletException {
46+
this.authenticationEntryPoint.commence(request, response, exception);
47+
}
48+
}
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.web.authentication;
17+
18+
import java.io.IOException;
19+
20+
import javax.servlet.Filter;
21+
import javax.servlet.FilterChain;
22+
import javax.servlet.ServletException;
23+
import javax.servlet.http.HttpServletRequest;
24+
import javax.servlet.http.HttpServletResponse;
25+
26+
import org.springframework.http.HttpStatus;
27+
import org.springframework.security.authentication.AuthenticationManager;
28+
import org.springframework.security.authentication.AuthenticationManagerResolver;
29+
import org.springframework.security.core.Authentication;
30+
import org.springframework.security.core.AuthenticationException;
31+
import org.springframework.security.core.context.SecurityContext;
32+
import org.springframework.security.core.context.SecurityContextHolder;
33+
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
34+
import org.springframework.security.web.util.matcher.RequestMatcher;
35+
import org.springframework.util.Assert;
36+
import org.springframework.web.filter.OncePerRequestFilter;
37+
38+
/**
39+
* A {@link Filter} that performs authentication of a particular request. An
40+
* outline of the logic:
41+
*
42+
* <ul>
43+
* <li>A request comes in and if it does not match
44+
* {@link #setRequestMatcher(RequestMatcher)}, then this filter does nothing and
45+
* the {@link FilterChain} is continued. If it does match then...</li>
46+
* <li>An attempt to convert the {@link HttpServletRequest} into an
47+
* {@link Authentication} is made. If the result is empty, then the filter does
48+
* nothing more and the {@link FilterChain} is continued. If it does create an
49+
* {@link Authentication}...</li>
50+
* <li>The {@link AuthenticationManager} specified in
51+
* {@link #GenericAuthenticationFilter(AuthenticationManager)} is used to
52+
* perform authentication.</li>
53+
* <li>The {@link AuthenticationManagerResolver} specified in
54+
* {@link #GenericAuthenticationFilter(AuthenticationManagerResolver)} is used
55+
* to resolve the appropriate authentication manager from context to perform
56+
* authentication.</li>
57+
* <li>If authentication is successful, {@link AuthenticationSuccessHandler} is
58+
* invoked and the authentication is set on {@link SecurityContextHolder}, else
59+
* {@link AuthenticationFailureHandler} is invoked</li>
60+
* </ul>
61+
*
62+
* @author Sergey Bespalov
63+
* @since 5.2.0
64+
*/
65+
public class AuthenticationFilter extends OncePerRequestFilter {
66+
67+
private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;
68+
private AuthenticationConverter authenticationConverter;
69+
private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
70+
private AuthenticationFailureHandler failureHandler = new AuthenticationEntryPointFailureHandler(
71+
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
72+
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
73+
74+
public AuthenticationFilter(AuthenticationManager authenticationManager,
75+
AuthenticationConverter authenticationConverter) {
76+
this((AuthenticationManagerResolver<HttpServletRequest>) r -> authenticationManager, authenticationConverter);
77+
}
78+
79+
public AuthenticationFilter(AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver,
80+
AuthenticationConverter authenticationConverter) {
81+
Assert.notNull(authenticationManagerResolver, "authenticationResolverManager cannot be null");
82+
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
83+
84+
this.authenticationManagerResolver = authenticationManagerResolver;
85+
this.authenticationConverter = authenticationConverter;
86+
}
87+
88+
public RequestMatcher getRequestMatcher() {
89+
return requestMatcher;
90+
}
91+
92+
public void setRequestMatcher(RequestMatcher requestMatcher) {
93+
Assert.notNull(requestMatcher, "requestMatcher cannot be null");
94+
this.requestMatcher = requestMatcher;
95+
}
96+
97+
public AuthenticationConverter getAuthenticationConverter() {
98+
return authenticationConverter;
99+
}
100+
101+
public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {
102+
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
103+
this.authenticationConverter = authenticationConverter;
104+
}
105+
106+
public AuthenticationSuccessHandler getSuccessHandler() {
107+
return successHandler;
108+
}
109+
110+
public void setSuccessHandler(AuthenticationSuccessHandler successHandler) {
111+
Assert.notNull(successHandler, "successHandler cannot be null");
112+
this.successHandler = successHandler;
113+
}
114+
115+
public AuthenticationFailureHandler getFailureHandler() {
116+
return failureHandler;
117+
}
118+
119+
public void setFailureHandler(AuthenticationFailureHandler failureHandler) {
120+
Assert.notNull(failureHandler, "failureHandler cannot be null");
121+
this.failureHandler = failureHandler;
122+
}
123+
124+
public AuthenticationManagerResolver<HttpServletRequest> getAuthenticationManagerResolver() {
125+
return authenticationManagerResolver;
126+
}
127+
128+
public void setAuthenticationManagerResolver(
129+
AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver) {
130+
Assert.notNull(authenticationManagerResolver, "authenticationManagerResolver cannot be null");
131+
this.authenticationManagerResolver = authenticationManagerResolver;
132+
}
133+
134+
@Override
135+
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
136+
throws ServletException, IOException {
137+
if (!requestMatcher.matches(request)) {
138+
filterChain.doFilter(request, response);
139+
return;
140+
}
141+
142+
try {
143+
Authentication authenticationResult = attemptAuthentication(request, response);
144+
if (authenticationResult == null) {
145+
filterChain.doFilter(request, response);
146+
return;
147+
}
148+
149+
successfulAuthentication(request, response, filterChain, authenticationResult);
150+
} catch (AuthenticationException e) {
151+
unsuccessfulAuthentication(request, response, e);
152+
}
153+
}
154+
155+
private void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
156+
AuthenticationException failed) throws IOException, ServletException {
157+
SecurityContextHolder.clearContext();
158+
failureHandler.onAuthenticationFailure(request, response, failed);
159+
}
160+
161+
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
162+
Authentication authentication) throws IOException, ServletException {
163+
SecurityContext context = SecurityContextHolder.createEmptyContext();
164+
context.setAuthentication(authentication);
165+
SecurityContextHolder.setContext(context);
166+
167+
successHandler.onAuthenticationSuccess(request, response, chain, authentication);
168+
}
169+
170+
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
171+
throws AuthenticationException, IOException, ServletException {
172+
Authentication authentication = authenticationConverter.convert(request);
173+
if (authentication == null) {
174+
return null;
175+
}
176+
177+
AuthenticationManager authenticationManager = authenticationManagerResolver.resolve(request);
178+
Authentication authenticationResult = authenticationManager.authenticate(authentication);
179+
if (authenticationResult == null) {
180+
throw new ServletException("AuthenticationManager should not return null Authentication object.");
181+
}
182+
183+
return authenticationResult;
184+
}
185+
186+
}

web/src/main/java/org/springframework/security/web/authentication/AuthenticationSuccessHandler.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.io.IOException;
1919

20+
import javax.servlet.FilterChain;
2021
import javax.servlet.ServletException;
2122
import javax.servlet.http.HttpServletRequest;
2223
import javax.servlet.http.HttpServletResponse;
@@ -38,6 +39,23 @@
3839
*/
3940
public interface AuthenticationSuccessHandler {
4041

42+
/**
43+
* Called when a user has been successfully authenticated.
44+
*
45+
* @param request the request which caused the successful authentication
46+
* @param response the response
47+
* @param chain the {@link FilterChain} which can be used to proceed other filters in the chain
48+
* @param authentication the <tt>Authentication</tt> object which was created during
49+
* the authentication process.
50+
* @since 5.2.0
51+
*/
52+
default void onAuthenticationSuccess(HttpServletRequest request,
53+
HttpServletResponse response, FilterChain chain, Authentication authentication)
54+
throws IOException, ServletException{
55+
onAuthenticationSuccess(request, response, authentication);
56+
chain.doFilter(request, response);
57+
}
58+
4159
/**
4260
* Called when a user has been successfully authenticated.
4361
*

0 commit comments

Comments
 (0)