Spring Cloud Security is a reliable security management module for building Spring Cloud microservices, where the HttpSecurity configuration can customize each application’s own security policies. In the HttpSecurity configuration, the authenticationEntryPoint configuration item is used to handle credentials error or access without corresponding permissions. Here is how this entry works.
configuration
Configuration authenticationEntryPoint inherits WebSecurityConfigurerAdapter class pay equal attention to write protected void the configure (HttpSecurity HttpSecurity) methods
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class TheSecurityConfig extends WebSecurityConfigurerAdapter {
private EntryPoint entryPoint;
private AccessDenied accessDenied;
public TheSecurityConfig(EntryPoint entryPoint, AccessDenied accessDenied){
this.entryPoint = entryPoint;
this.accessDenied = accessDenied;
}
@Bean
public TokenAuthFilter tokenAuthFilter(a) throws Exception {
TokenAuthFilter tokenAuthFilter = new TokenAuthFilter(authenticationManager());
return tokenAuthFilter;
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception{
httpSecurity
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// Block rules
.and()
.authorizeRequests()
.anyRequest().authenticated()
// Unauthorized processing
.and()
.exceptionHandling()
.authenticationEntryPoint(entryPoint)
.accessDeniedHandler(accessDenied)
.and()
// Customize the token resolution filter to obtain permission and role information.addFilter(tokenAuthFilter()) .csrf().disable(); }}Copy the code
The preceding configuration uses a customized authentication interface. Therefore, the formLogin item is not configured. EntryPoint AccessDenied TokenAuthFilter can be roughly in the following format
@Component
public class EntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
// Write response to return content}}Copy the code
@Component
public class AccessDenied implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
// Write response to return content}}Copy the code
public class TokenAuthFilter extends BasicAuthenticationFilter {
public TokenAuthFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String token = request.getHeader("Authorization");
UsernamePasswordAuthenticationToken authRequest = null;
// Custom parse token Stores successful parse information into authRequest
/ / like authRequest = new UsernamePasswordAuthenticationToken (username and token, authorities);
// authorities is a Collection of type
that stores data of type SimpleGrantedAuthority and is the role or permission information of the request
if(authRequest ! =null) { SecurityContextHolder.getContext().setAuthentication(authRequest); } chain.doFilter(request, response); }}Copy the code
Request Filtering process
When the request enters the service, run internalDoFilter in ApplicationFilterChain. The core code is as follows
private void internalDoFilter(ServletRequest request,ServletResponse response) throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
// ...
try {
// ...
if( Globals.IS_SECURITY_ENABLED ) {
// ...
} else {
// Run the filter
filter.doFilter(request, response, this); }}catch (IOException | ServletException | RuntimeException e) {
// Exception catch and throw
throw e;
} catch (Throwable e) {
// ...
}
return;
}
// We fell off the end of the chain -- call the servlet instance
try {
// ...
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED ) {
// ...
} else {
// After the filter runs, it enters the serviceservlet.service(request, response); }}catch (IOException | ServletException | RuntimeException e) {
// Exception catch and throw
throw e;
} catch (Throwable e) {
// ...
} finally {
// ...}}Copy the code
In ExceptionTranslationFilter this filter, there is an exception to capture
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
}
catch (IOException ex) {
throw ex;
}
catch (Exception ex) {
// ...
if (securityException == null) {
securityException = (AccessDeniedException) this.throwableAnalyzer
.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
}
// ...handleSpringSecurityException(request, response, chain, securityException); }}private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, RuntimeException exception) throws IOException, ServletException {
if (exception instanceof AuthenticationException) {
// ...
}
else if (exception instanceofAccessDeniedException) { handleAccessDeniedException(request, response, chain, (AccessDeniedException) exception); }}private void handleAccessDeniedException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AccessDeniedException exception) throws ServletException, IOException {
// ...
if (isAnonymous || this.authenticationTrustResolver.isRememberMe(authentication)) {
// ...
sendStartAuthentication(request, response, chain,
new InsufficientAuthenticationException(this.messages.getMessage("ExceptionTranslationFilter.insufficientAuthentication"."Full authentication is required to access this resource")));
}
else {
// ...
this.accessDeniedHandler.handle(request, response, exception); }}protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException {
// ...
// Here is the configured authenticationEntryPoint trigger location
this.authenticationEntryPoint.commence(request, response, reason);
}
Copy the code
Source by above knowable, after if throw AccessDeniedException and has not been ExceptionTranslationFilter filter capture and processing, The sendStartAuthentication handler for that filter is reached and the authenticationEntryPoint method is called, This method is used to configure the authenticationEntryPoint entry method in httpSecurity. If this method is not configured, the request returns nothing.
Exception is thrown
Type of FilterOrderRegistration registered after ExceptionTranslationFilter FilterSecurityInterceptor filter, The filter inherited the AbstractSecurityInterceptor class in its doFilter method, calls the invoke method, this method has the following code:
// Check whether the request can be allowed. If not, throw an AccessDeniedException
InterceptorStatusToken token = super.beforeInvocation(filterInvocation);
try {
filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
}
finally {
super.finallyInvocation(token);
}
Copy the code
If thrown in the code above AccessDeniedException is unusual, because there is no capture process the wrong way, it will throw up a filter, if we don’t change the filter order, will be done in ExceptionTranslationFilter capture and processing. If the request is released, the FilterSecurityInterceptor never throw an exception, will go to the next filter in the class until internalDoFilter servlet. Service (request, response); Methods. If you add comments such as @preauthorize (“hasAuthority(‘admin’)”) to your request methods, Will be in the class to the AbstractSecurityInterceptor attemptAuthorization raises AccessDeniedException error in the method, the same, If no capture in the filter after ExceptionTranslationFilter processing, can also go to sendStartAuthentication method for processing.
Custom capture filters
Understand that above AccessDeniedException will be ExceptionTranslationFilter capture, But if we add a after ExceptionTranslationFilter filter handle AccessDeniedException error filter, does not need to configure the AuthenticationEntryPoint entrance. Such filters can be of the following form:
public class SomeFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try{
chain.doFilter(request, response);
} catch (Exception e){
if(e instanceof AccessDeniedException){
// Write a response to return content or other processing}}}Copy the code
Accordingly, httpSecurity need to add. AddFilterAfter (SomeFilter (), ExceptionTranslationFilter. Class) configuration. In this way, you can dispense with the AuthenticationEntryPoint entry, but this is not necessary and it is recommended to use the AuthenticationEntryPoint entry configuration. Presented here is only a reference method, or the need to pay attention to not add a capture after ExceptionTranslationFilter AccessDeniedException exception filters, This invalidates the AuthenticationEntryPoint entry method and should continue to be thrown up if caught.