I need to configure the Spring Security so that users that have either Basic Auth or API Key can access the API in the absence of one other. The below code works for Basic Auth but when I switch to API Key in Postman, I can not access the API using the correct API key/value added to the Header in Postman in the Authorization tab and I am getting 401.
However, when I just use ApiKeyWebSecurityConfigurerAdapter by itself, I can access the API even with the wrong API key/value pair. Any help would be appreciated.
Note: I used this and https://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/html5/#multiple-httpsecurity as reference.
Security Config:
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.userDetailsService(userDetailsService())
.httpBasic(Customizer.withDefaults());
}
#Bean
public UserDetailsService userDetailsService() {
return new JpaUserDetailService();
}
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
#Configuration
public static class ApiKeyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Value("${myApp.http.auth-token-header-name}")
private String principalRequestHeader;
#Value("${myApp.http.auth-token}")
private String principalRequestValue;
#Override
protected void configure(HttpSecurity http) {
http
.csrf()
.disable();
APIKeyAuthFilter filter = new APIKeyAuthFilter(principalRequestHeader);
filter.setAuthenticationManager(new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String principal = (String) authentication.getPrincipal();
if (!principalRequestValue.equals(principal)) {
throw new BadCredentialsException("The API key was not found or not the expected value.");
}
authentication.setAuthenticated(true);
return authentication;
}
});
}
}
}
Filter:
public class APIKeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
private String principalRequestHeader;
public APIKeyAuthFilter(String principalRequestHeader) {
this.principalRequestHeader = principalRequestHeader;
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
return request.getHeader(principalRequestHeader);
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "N/A";
}
}
Related
I'm making an app with Spring security using JWT and react. However, it is behaving strangely. In the Configure file, which I did according to a tutorial, it says I need to ignore only the login paths. It works, I can login but after I obtain the token the config swears at me saying I can't access the path... I have the token now! How to fix it? My config files:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtUnAuthorizedResponseAuthenticationEntryPoint jwtUnAuthorizedResponseAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtInMemoryUserDetailsService;
#Autowired
private JwtTokenAuthorizationOncePerRequestFilter jwtAuthenticationTokenFilter;
#Value("${jwt.get.token.uri}")
private String authenticationPath;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(jwtInMemoryUserDetailsService)
.passwordEncoder(passwordEncoderBean());
}
#Bean
public PasswordEncoder passwordEncoderBean() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(jwtUnAuthorizedResponseAuthenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().authenticated();
httpSecurity
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
httpSecurity
.headers()
.frameOptions().sameOrigin() //H2 Console Needs this setting
.cacheControl(); //disable caching
}
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity
.ignoring()
.antMatchers(
HttpMethod.POST,
authenticationPath
)
.antMatchers(HttpMethod.OPTIONS, "/**")
.and()
.ignoring()
.antMatchers(
HttpMethod.GET,
"/map-runner/", "/map-runner/static/**", "/static/**", "/map-runner/login", "/", "/login"
);
}
}
and
#Configuration
public class Config implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
ResourceResolver resolver = new ReactResourceResolver();
registry.addResourceHandler("/**")
.resourceChain(true)
.addResolver(resolver);
}
public class ReactResourceResolver implements ResourceResolver {
private static final String REACT_DIR = "/static/";
private static final String REACT_STATIC_DIR = "static";
private Resource index = new ClassPathResource(REACT_DIR + "index.html");
private List<String> rootStaticFiles = Arrays.asList("favicon.ico",
"asset-manifest.json", "manifest.json", "service-worker.js");
#Override
public Resource resolveResource(HttpServletRequest request, String requestPath,
List<? extends Resource> locations, ResourceResolverChain chain) {
return resolve(requestPath, locations);
}
#Override
public String resolveUrlPath(String resourcePath, List<? extends Resource> locations, ResourceResolverChain chain) {
Resource resolvedResource = resolve(resourcePath, locations);
if (resolvedResource == null) {
return null;
}
try {
return resolvedResource.getURL().toString();
} catch (IOException e) {
return resolvedResource.getFilename();
}
}
private Resource resolve(String requestPath, List<? extends Resource> locations) {
if (requestPath == null) return null;
if (rootStaticFiles.contains(requestPath)
|| requestPath.startsWith(REACT_STATIC_DIR)) {
return new ClassPathResource(REACT_DIR + requestPath);
} else
return index;
}
}
}
I also use the server.servlet.context-path=/map-runner path in the properties.
The JWT implementation in other files is like in this tutorial repo https://github.com/in28minutes/spring-boot-react-fullstack-examples/tree/master/spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main
I have created an OAuth Authorization Server using default spring boot configurations, where the client is redirected to the auto-generated login page, userDetailsService looks up the User table and authenticates, and after successful authentication the server returns a jwt token. Now I want customize this and change two things but I am having difficulty in doing it.
1) Use my own login.jsp page instead of the auto-generated login page so I can have an extra field(eg. dropdownlist) and use this along with the username and password for authentication since I have different user tables
2) Instead of using the default UserDetailsService I am trying to implement my own AuthenticationProvider, this is because I have multiple users table and want to search for user in the correct table based on the value from the extra field (dropdown list mentioned in 1). Also how to get the dropdownlist value in the AuthenticationProvider?
In my properties file I have set:
spring.mvc.view.prefix: /WEB-INF/jsp/ and
spring.mvc.view.suffix: .jsp
#Configuration
#EnableWebSecurity
public class ServerWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new DefaultAuthenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize")
.and()
.authorizeRequests()
.anyRequest().authenticated();
}
}
#Configuration
#EnableAuthorizationServer
#Import(ServerWebSecurityConfig.class)
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter{
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
#Qualifier("dataSource")
private DataSource dataSource;
#Autowired
private UserDetailsService userDetailsService;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore()).accessTokenConverter(accessTokenConverter()).authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("permitAll()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
clients.withClientDetails(jdbcClientDetailsService);
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("secret");
return converter;
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
#Bean
public PasswordEncoder userPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
public class DefaultAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication.getName() == null || authentication.getCredentials() == null
|| authentication.getName().isEmpty() || authentication.getCredentials().toString().isEmpty()) {
return null;
}
final String userName = authentication.getName();
final String password = (String) authentication.getCredentials();
// final String userTable = how to get this?
// make db query in correct table based on value of userTable
User user = null;
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
for (UserAuthority authority : user.getUserAuthorities()) {
authorities.add(new CustomGrantedAuthority(authority.getAuthority().getName()));
}
Map<String, String> userDetails = new HashMap<>();
userDetails.put("username", userName);
return new UsernamePasswordAuthenticationToken(userDetails, password, authorities);
}
#Override
public boolean supports(Class<?> authentication) {
return false;
}
}
#Controller
public class OAuthController {
#RequestMapping("/login")
public String login() {
return "login";
}
}
I am expecting that my client app is redirected to the custom login page, once login button is pressed my custom AuthenticationProvider will lookup for user in the correct table based on the extra field in the custom login page.
For the first point you just have to configure your custom login-path for the login in your WebSecurityConfigurerAdapter.
Add .formLogin().loginPage("/login").permitAll()
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize")
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.authorizeRequests()
.anyRequest().authenticated();
}
In WebSecurityConfigurerAdapter class we have protected void configure(HttpSecurity http) method where we add restrictions to request. What i want is:
1.Retrieve a user who wants to enter
2.Check if that user is present in db
3.Check if that user has a special propery, for example.: login.endsWith('_login')
All you need to implement Custom AuthenticationProvider. Do the authentication code by other server as you need.
#Component
public class CustomAuthenticationProvider
implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
if (shouldAuthenticateAgainstThirdPartySystem()) {
// use the credentials
// and authenticate against the third-party system
return new UsernamePasswordAuthenticationToken(
name, password, new ArrayList<>());
} else {
return null;
}
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(
UsernamePasswordAuthenticationToken.class);
}
}
And register your Authentication provider.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider authProvider;
#Override
protected void configure(
AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic();
}
}
Note : I am assuming you're using HTTP Basic Authentication, you can to
change it to FormLogin or as per your need.
I have a web application with 2 types of resources.
web pages
web services
I want to secure the web pages using one authentication provider (i.e. CAS) and the web services using another authentication provider (i.e. BASIC authentication).
I found a solution which could work here, but it uses XML, and I would prefer to not use XML configuration if possible.
Is there a Java Config solution to this?
Well it took a while to figure out how to do it...
Basically I split up my original security configuration class into 3 separate configuration classes.
This is basically how I did it...
The main security configuration...
#Configuration
#Import({WebPageSecurityConfig.class, WebServiceSecurityConfig.class})
public class SecurityConfig {
}
The security configuration for web pages... (URL does not begin with /service/**)
#Configuration
#Order(200)
#EnableWebMvcSecurity
public class WebPageSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(casAuthenticationProvider());
}
#Override
public void configure(final HttpSecurity http) throws Exception {
http.csrf().disable();
http.requestMatcher(new RequestMatcher() {
#Override
public boolean matches(final HttpServletRequest request) {
final String url = request.getServletPath() + StringUtils.defaultString(request.getPathInfo());
return !(url.startsWith("/service/"));
}
});
http.addFilter(casAuthenticationFilter()).exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint());
http.authorizeRequests().
antMatchers("/securedPage").hasAuthority("ROLE_CAS_USER"). // /securedPage can only be accessed by cas user
anyRequest().permitAll(); // all other pages are unsecured
}
// General Application Security (CAS Authentication)
#Bean
public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
final CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
casAuthenticationFilter.setAuthenticationManager(authenticationManager());
return casAuthenticationFilter;
}
#Bean
public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
final CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
casAuthenticationEntryPoint.setLoginUrl(env.getRequiredProperty("cas.server.url") + "/login");
casAuthenticationEntryPoint.setServiceProperties(casServiceProperties());
return casAuthenticationEntryPoint;
}
#Bean
public ServiceProperties casServiceProperties() {
final ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService(env.getRequiredProperty("cas.service.url") + "/j_spring_cas_security_check");
serviceProperties.setSendRenew(false);
return serviceProperties;
}
#Bean
public CasAuthenticationProvider casAuthenticationProvider() {
final CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
casAuthenticationProvider.setAuthenticationUserDetailsService(casAuthenticationUserDetailsService());
casAuthenticationProvider.setServiceProperties(casServiceProperties());
casAuthenticationProvider.setTicketValidator(casTicketValidator());
casAuthenticationProvider.setKey("casAuthenticationProviderKey");
casAuthenticationProvider.setStatelessTicketCache(casStatelessTicketCache());
return casAuthenticationProvider;
}
#Bean
public AuthenticationUserDetailsService casAuthenticationUserDetailsService() {
final AbstractCasAssertionUserDetailsService authenticationUserDetailsService = new AbstractCasAssertionUserDetailsService() {
#Override
protected UserDetails loadUserDetails(final Assertion assertion) {
final String username = assertion.getPrincipal().getName();
final List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_CAS_USER"));
return new User(username, "notused", authorities);
}
};
return authenticationUserDetailsService;
}
#Bean
public TicketValidator casTicketValidator() {
final Saml11TicketValidator ticketValidator = new Saml11TicketValidator(env.getRequiredProperty("cas.server.url"));
ticketValidator.setTolerance(env.getRequiredProperty("cas.ticket.tolerance", Long.class));
return ticketValidator;
}
#Bean
public StatelessTicketCache casStatelessTicketCache() {
final EhCacheBasedTicketCache ticketCache = new EhCacheBasedTicketCache();
ticketCache.setCache(casCache());
return ticketCache;
}
#Bean(initMethod = "initialise", destroyMethod = "dispose")
public Cache casCache() {
final Cache cache = new Cache("casTickets", 50, true, false, 3600, 900);
return cache;
}
#Autowired
private Environment env;
}
The security configuration for RESTful web services (URL starts with /service/**)
#Configuration
#Order(300)
#EnableWebMvcSecurity
public class WebServiceSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().
withUser("admin").password("password").authorities(new SimpleGrantedAuthority("ROLE_WS_USER"));
}
#Override
public void configure(final HttpSecurity http) throws Exception {
http.csrf().disable();
http.
antMatcher("/service/**"). // only process URLs that begin with /service/
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and(). // RESTful web services are stateless
addFilter(wsAuthenticationFilter()).exceptionHandling().authenticationEntryPoint(wsAuthenticationEntryPoint());
http.authorizeRequests().anyRequest().hasAuthority("ROLE_WS_USER"); // all requests are secured
}
// Web Service Security (BASIC Authentication)
#Bean
public BasicAuthenticationFilter wsAuthenticationFilter() throws Exception {
final BasicAuthenticationFilter wsAuthenticationFilter = new BasicAuthenticationFilter(authenticationManager(), wsAuthenticationEntryPoint());
return wsAuthenticationFilter;
}
#Bean
public BasicAuthenticationEntryPoint wsAuthenticationEntryPoint() {
final BasicAuthenticationEntryPoint wsAuthenticationEntryPoint = new BasicAuthenticationEntryPoint();
wsAuthenticationEntryPoint.setRealmName("My Realm");
return wsAuthenticationEntryPoint;
}
#Autowired
private Environment env;
}
It's explain how to create multiple securities in the docs
http://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html#multiple-httpsecurity
something like this should work
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Resource private UserDetailsService userBasicAuthService;
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**")
.userDetailsService(userBasicAuthService)
.authorizeRequests()
.and()
.httpBasic();
}
}
#Configuration
public static class PagesWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Resource private UserDetailsService userCasService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/pages/**")
.userDetailsService(userCasService)
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
}
On user authentication i need to retrieve his remote address and remote host.
I'm trying to implement a custom filter to support this, but i'm getting "authenticationManager must be specified".
Another doubt is... What is the correct way to register a custom filter using programmatically ?
Configuration using annotations:
#Configuration
#EnableWebSecurity
public class SecurityApplicationConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private SCAAuthenticationFilter scaAuthenticationFilter;
#Autowired
private SCAAuthenticationProvider scaAuthenticationProvider;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(scaAuthenticationProvider);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilter(scaAuthenticationFilter) // What is the right way ?
.addFilterBefore(scaAuthenticationFilter, AbstractAuthenticationProcessingFilter.class) // What is the right way ?
.csrf().disable()
.authorizeRequests()
.antMatchers("/manual/**").authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.failureUrl("/login?error=true")
.defaultSuccessUrl("/manual")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.permitAll()
.and();
}
}
The custom filter:
#Component
public class SCAAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
String remoteHost = request.getRemoteHost();
String remoteAddr = request.getRemoteAddr();
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
SCAAuthenticationToken scaAuthenticationToken = new SCAAuthenticationToken(username, password, remoteHost, remoteAddr);
setDetails(request, scaAuthenticationToken);
return getAuthenticationManager().authenticate(scaAuthenticationToken);
}
}
You need set a authenticationManagerBean for your extended filter and config it corr
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public ExUsernamePasswordAuthenticationFilter exUsernamePasswordAuthenticationFilter()
throws Exception {
ExUsernamePasswordAuthenticationFilter exUsernamePasswordAuthenticationFilter = new ExUsernamePasswordAuthenticationFilter();
exUsernamePasswordAuthenticationFilter
.setAuthenticationManager(authenticationManagerBean());
return exUsernamePasswordAuthenticationFilter;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
RequestMatcher requestMatcher = new RequestMatcher() {
#Override
public boolean matches(HttpServletRequest httpServletRequest) {
if (httpServletRequest.getRequestURI().indexOf("/api", 0) >= 0) {
return true;
}
return false;
}
};
http
.addFilterBefore(exUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
...
}
}
Your custom filter extends Spring Security's UsernamePasswordAuthenticationFilter, which means it needs a reference to the authentication manager. I would create your filter as an #Bean in the security configuration, then follow this answer which explains different options for getting a reference to the AuthenticationManager.
In the class that is extending WebSecurityConfigurerAdapter, override the authenticationManagerBean() method and annotate it with #Bean as such:
#Configuration
#EnableWebMvcSecurity
public class YourCustomConfig extends WebSecurityConfigurerAdapter{
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Now you will be able to #Autowire the AuthenticationManager in other classes.
Another option is to create a configurer for your filter and delegate all the work concerning filter initialization to it (the same way UsernamePasswordAuthenticationFilter is configured through the FormLoginConfigurer and AbstractAuthenticationProcessingFilter is configured by the AbstractAuthenticationFilterConfigurer).
public class SCAAuthenticationConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>() {
public static SCAAuthenticationConfigurer scaAuthentication() {
return new SCAAuthenticationConfigurer()
}
#Override
public void configure(HttpSecurity http) throws Exception {
SCAAuthenticationFilter filter = new SCAAuthenticationFilter()
filter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
// add postProcess(filter) call if require to autowire some fields
http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class)
}
}
Having such configurer your SecurityConfig will be looking more tidy:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.apply(scaAuthentication())
.and()
// do the rest of configuration
}
}
You may even delegate filter initialization to the ApplicationContext (for example, if you have configuration to inject):
public class FilterWithSettingsConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>() {
#Configuration
#EnableConfigurationProperties(SomeSettings.class)
private static class Config {}
#Override
public void configure(HttpSecurity http) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()
context.parent = http.getSharedObject(ApplicationContext.class)
context.register(Config.class)
context.refresh()
FilterWithSettings filter =
context.getAutowireCapableBeanFactory().createBean(FilterWithSettings.class)
filter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class)
}
}
For the comprehensive example take a look at the https://github.com/shiver-me-timbers/smt-spring-security-parent/blob/master/smt-spring-security-jwt/src/main/java/shiver/me/timbers/spring/security/JwtSpringSecurityAdaptor.java