sequence
In this paper, several ways to customize the look of spring security
Main way
- Custom UserDetailsService
- Custom passwordEncoder
- Custom filter
- Custom AuthenticationProvider
- Custom AccessDecisionManager
- Custom securityMetadataSource
- Custom access control access
- Custom authenticationEntryPoint
- Customize more WebSecurityConfigurerAdapter
Custom UserDetailsService
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//......
@Bean
@Override
protected UserDetailsService userDetailsService(){
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("demoUser1").password("123456")
.authorities("ROLE_USER","read_x").build());
manager.createUser(User.withUsername("admin").password("123456")
.authorities("ROLE_ADMIN").build());
return manager;
}
}
By rewriting userDetailsService () method custom userDetailsService. Shown here is InMemoryUserDetailsManager.
spring security built JdbcUserDetailsManager, you can expand their own
Custom passwordEncoder
Since encryption password defined, the following examples
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//......
@Bean
public DaoAuthenticationProvider authenticationProvider() {
final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(11);
}
}
Custom filter
Custom filter can not do without a built-in filter cognitive order for spring security:
Standard Filter Aliases and Ordering
Various spring security filter built in the following order:
Alias | Filter Class | Namespace Element or Attribute |
---|---|---|
CHANNEL_FILTER | ChannelProcessingFilter | http/intercept-url@requires-channel |
SECURITY_CONTEXT_FILTER | SecurityContextPersistenceFilter | http |
CONCURRENT_SESSION_FILTER | ConcurrentSessionFilter | session-management/concurrency-control |
HEADERS_FILTER | HeaderWriterFilter | http/headers |
CSRF_FILTER | CsrfFilter | http/csrf |
LOGOUT_FILTER | LogoutFilter | http/logout |
X509_FILTER | X509AuthenticationFilter | http/x509 |
PRE_AUTH_FILTER | AbstractPreAuthenticatedProcessingFilter Subclasses | N/A |
CAS_FILTER | CasAuthenticationFilter | N/A |
FORM_LOGIN_FILTER | UsernamePasswordAuthenticationFilter | http/form-login |
BASIC_AUTH_FILTER | BasicAuthenticationFilter | http/http-basic |
SERVLET_API_SUPPORT_FILTER | SecurityContextHolderAwareRequestFilter | http/@servlet-api-provision |
JAAS_API_SUPPORT_FILTER | JaasApiIntegrationFilter | http/@jaas-api-provision |
REMEMBER_ME_FILTER | RememberMeAuthenticationFilter | http/remember-me |
ANONYMOUS_FILTER | AnonymousAuthenticationFilter | http/anonymous |
SESSION_MANAGEMENT_FILTER | SessionManagementFilter | session-management |
EXCEPTION_TRANSLATION_FILTER | ExceptionTranslationFilter | http |
FILTER_SECURITY_INTERCEPTOR | FilterSecurityInterceptor | http |
SWITCH_USER_FILTER | SwitchUserFilter | N/A |
Built-in authentication filter
- UsernamePasswordAuthenticationFilter
Parameters username, password, and take UsernamePasswordAuthenticationFilter, parameter extraction structure UsernamePasswordAuthenticationToken authentication, then fill SecurityContextHolder success of Authentication
- BasicAuthenticationFilter
header inside there Authorization, and value begins with Basic, then go BasicAuthenticationFilter, parameter extraction structure UsernamePasswordAuthenticationToken authentication, then fill SecurityContextHolder success of Authentication
- AnonymousAuthenticationFilter
The user is not logged in, to fill AnonymousAuthenticationToken SecurityContextHolder of Authentication
Define your own filter
You can inherit GenericFilterBean like UsernamePasswordAuthenticationFilter or AnonymousAuthenticationFilter, or, as BasicAuthenticationFilter inherited OncePerRequestFilter.
About GenericFilterBean difference with OncePerRequestFilter can see this in the spring mvc several types of interceptors contrast
Custom filter to complete the main functions are as follows:
- Extracting authentication parameters
- Call authentication, success is filled SecurityContextHolder of Authentication, failure exception is thrown
Examples
public class DemoAuthFilter extends GenericFilterBean {
private final AuthenticationManager authenticationManager;
public DemoAuthFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
String token = httpServletRequest.getHeader("app_token");
if(StringUtils.isEmpty(token)){
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "invalid token");
return ;
}
try {
Authentication auth = authenticationManager.authenticate(new WebToken(token));
SecurityContextHolder.getContext().setAuthentication(auth);
filterChain.doFilter(servletRequest, servletResponse);
} catch (AuthenticationException e) {
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
}
}
}
Set filter order
After completion of the above defined filter, then it must be placed into the filterChain
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//......
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new DemoAuthFilter(authenticationManager()), BasicAuthenticationFilter.class);
http.csrf().disable();
http.logout().disable();
http.sessionManagement().disable();
}
}
Here he added before BasicAuthenticationFilter, of course, can be replaced according to the situation directly UsernamePasswordAuthenticationFilter
http.addFilterAt(new DemoAuthFilter(authenticationManager()),UsernamePasswordAuthenticationFilter.class);
Custom AuthenticationProvider
AuthenticationManager interfaces have achieved the equivalent of a ProviderManager provider chain, inside which there is a List <AuthenticationProvider> providers, to achieve authentication provider.
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
Authentication result = null;
boolean debug = logger.isDebugEnabled();
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
//......
try {
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException e) {
prepareException(e, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to
// invalid account status
throw e;
}
catch (InternalAuthenticationServiceException e) {
prepareException(e, authentication);
throw e;
}
catch (AuthenticationException e) {
lastException = e;
}
}
//......
}
AuthenticationProvider by supports methods to identify whether it can handle this type of Authentication.
AnonymousAuthenticationFilter configuration is AnonymousAuthenticationToken, processed by AnonymousAuthenticationProvider
public class AnonymousAuthenticationProvider implements AuthenticationProvider,
MessageSourceAware {
//......
public boolean supports(Class<?> authentication) {
return (AnonymousAuthenticationToken.class.isAssignableFrom(authentication));
}
}
UsernamePasswordAuthenticationFilter, BasicAuthenticationFilter configuration is UsernamePasswordAuthenticationToken, of the DaoAuthenticationProvider (parent class AbstractUserDetailsAuthenticationProvider) treated
public abstract class AbstractUserDetailsAuthenticationProvider implements
AuthenticationProvider, InitializingBean, MessageSourceAware {
//......
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication));
}
}
We like the above customized WebToken, examples of which are:
Authentication can implement an interface, or inherit AbstractAuthenticationToken
public class WebToken extends AbstractAuthenticationToken {
private final String token;
public WebToken(String token) {
super(null);
this.token = token;
}
@Override
public Object getCredentials() {
return this.token;
}
@Override
public Object getPrincipal() {
return null;
}
}
Here it is a custom look to support such WebToken of AuthenticationProvider
AuthenticationProvider to achieve the function according to the parameters to verify that you can log on through, not through an exception is thrown; it is acquired by the authentication GrantedAuthority fill in
if it is inherited AbstractAuthenticationToken, is filling its authorities attribute
the previous custom DemoAuthFilter will after successful login, the authentication is written to the context in SecurityContextHolder
can be achieved AuthenticationProvider interface, or inherited AbstractUserDetailsAuthenticationProvider (默认集成了preAuthenticationChecks以及postAuthenticationChecks
)
@Service
public class MyAuthProvider implements AuthenticationProvider {
//...
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//......
}
@Override
public boolean supports(Class<?> authenticationClass) {
return return (WebToken.class
.isAssignableFrom(authenticationClass));
}
}
Custom AccessDecisionManager
Front filter processing login problems, then specify whether the resource can be accessed by the FilterSecurityInterceptor problem to deal with. The FilterSecurityInterceptor is used to authenticate the AccessDecisionManager.
Several AccessDecisionManager implementation:
- AffirmativeBased(
spring security默认使用
)
As long as there is cast by (ACCESS_GRANTED) votes, directly ruled by. If you do not vote by vote and the opposition (ACCESS_DENIED) ticket in one and above, not directly ruled by.
- ConsensusBased(
少数服从多数
)
By the number of votes is greater than the penalty for the votes against by; by votes is less than the penalty for the votes against not pass; by votes equal votes for and against, can be performed by determining whether a configuration allowIfEqualGrantedDeniedDecisions (the default is true).
- UnanimousBased(
反对票优先
)
No matter how many voters voted by how much (ACCESS_GRANTED) ticket, as long as there against (ACCESS_DENIED), that it is not ruled by; and if there is no negative vote cast by voters vote, then ruled by.
Examples
One of its self-defined and can refer to talk about the role hierarchy spring security , showing how to customize AccessDecisionVoter.
Custom securityMetadataSource
Mainly achieved by custom ObjectPostProcessor, specific examples refer spring security permissions dynamic configuration url
Custom access control access
AuthorizeRequests control can be used permitAll, anonymous, authenticated, hasAuthority, hasRole etc.
.antMatchers("/login","/css/**", "/js/**","/fonts/**","/file/**").permitAll()
.antMatchers("/anonymous*").anonymous()
.antMatchers("/session").authenticated()
.antMatchers("/login/impersonate").hasAuthority("ROLE_ADMIN")
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/auth/*").hasAnyRole("ADMIN","USER")
These are built using the spring security of expression. Like hasAuthority, etc., they still achieved using internal access methods. So we can directly access, to achieve maximum customization.
Examples
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login/**","/logout/**")
.permitAll()
.anyRequest().access("@authService.canAccess(request,authentication)");
}
}
This is a bit like using a spring EL expressions to achieve the following examples
@Component
public class AuthService {
public boolean canAccess(HttpServletRequest request, Authentication authentication) {
Object principal = authentication.getPrincipal();
if(principal == null){
return false;
}
if(authentication instanceof AnonymousAuthenticationToken){
//check if this uri can be access by anonymous
//return
}
Set<String> roles = authentication.getAuthorities()
.stream()
.map(e -> e.getAuthority())
.collect(Collectors.toSet());
String uri = request.getRequestURI();
//check this uri can be access by this role
return true;
}
}
Custom authenticationEntryPoint
For example, you want to change the basic certification realmName, in addition to further specify the spring security configuration
security.basic.realm=myrealm
It can be so
httpBasic().authenticationEntryPoint(createBasicAuthEntryPoint("myrealm"))
public static BasicAuthenticationEntryPoint createBasicAuthEntryPoint(String realmName){
BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();
entryPoint.setRealmName(realmName);
return entryPoint;
}
Customize more WebSecurityConfigurerAdapter
spring security does not support the use of antMatchers not, so you can customize many WebSecurityConfigurerAdapter, priority use order to achieve matching of coverage, specifically refer to this article Multiple Entry Points in Spring Security
summary
There are other ways to customize, and other follow-up have found supplemented on.