Spring boot (v2.6.7) security with JWT, web.ignoring() is working but not antMatchers permitAll() - spring-boot

I migrated Spring boot from 2.1.5 to 2.6.7. Now the security configuration is throwing a warning about antMatchers in web.ignoring in the WebSecurity configuration lines, although it works correctly. Upon moving the web.ignoring to antMatchers permitAll() in httpConfiguration, the warning goes away but the urls are no longer permitted.
I am also using JWT and it looks like the JWT filters are kicking in and not honoring the httpConfiguration. I am really confused as to how to configure this. I am including the before and after here.
WORKS WITH WARNING:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfiguration.class);
#Override
public void init(WebSecurity web) throws Exception {
super.init(web);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
LOGGER.debug("**********************SECURITY INITALIZE NORMAL#######################################");
http.cors().and().csrf().disable().authorizeRequests()
.anyRequest().authenticated().and()
.addFilter(new JWTAuthorizationFilter(authenticationManager(), getApplicationContext()))
.addFilter(new JWTAuthenticationFilter(authenticationManager(), getApplicationContext()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/api-docs/**");
}
}
DOES NOT WORK:
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfiguration.class);
#Override
public void init(WebSecurity web) throws Exception {
super.init(web);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
LOGGER.debug("**********************SECURITY INITALIZE NORMAL#######################################");
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers("/v2/api-docs/**").permitAll() // ADDED LINE
.anyRequest().authenticated().and()
.addFilter(new JWTAuthorizationFilter(authenticationManager(), getApplicationContext()))
.addFilter(new JWTAuthenticationFilter(authenticationManager(), getApplicationContext()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(WebSecurity web) throws Exception {
//web.ignoring().antMatchers("/v2/api-docs/**");
}
}
Looks like somehow the configuration breaks when using http security versus web security. I have tried many of the suggestions but nothing seems to work.
Can someone familiar with Spring Boot Security in 2.6.7 with JWT filters please suggest what I am doing wrong?

Related

How to import security configuration class from another module

I want to develop my security config in separate module and add it as dependency in main app. so I wrote a very simple test project as parent which has 2 child modules: security-module and app
security-module has only one configuration class as follow:
#Configuration
#EnableWebSecurity
public class Security extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().
withUser("h").
password("1").
roles("admin");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().disable();
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/", "/login").permitAll()
.antMatchers("/static/**").permitAll()
.anyRequest().authenticated();
}
}
In app module, I have a SecurityConfig class which is as follow:
#Configuration
#Import({
Security.class
})
public class SecurityConfig{
}
also the resources directory is as follow:
The problem is HttpSecurity object, configured in Security class, is not registered completely. Said completely cause if I add anyRequest().pertmitAll() in Security class, then all is good, but now for any request i get 403 error, even when I call resources explicity like localhost:8080/dash.html
I also tried extending Security as follow in SecurityConfig:
#Configuration
public class SecurityConfig extends Security{
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
}
}
but nothing changed.
How can I fix this problem?

Spring Boot Security Configuration is not working

I have two types of urls one are secured and one are not secured like registration and SignIn
I want "registration" and "SignIn" to byPass security and filters while all the other urls must pass through filters and security.
Following is my Security Configuration code but it is not working.
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#Configuration
public class AppSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Autowired
TempTokenGenerator tempTokenGenerator;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(getPasswordEncoder());
}
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("notsecured/signin");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests()
.antMatchers("/", "**secured/**").authenticated()
.and().logout().permitAll()
.and()
.apply(new TempConfigurer(tempTokenGenerator));
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
what am I missing? What should I do to include "secured" urls in authentication and filters while excluding "unsecured" from authentication and filter.
web.ignoring().antMatchers("notsecured/signin");
seems not working if I put
.anyRequest().authenticated()
with
http.authorizeRequests() to make secured urls work.
and if I put
.antMatchers("/","**/secured/**").authenticated()
with
.anyRequest().permitAll()
it is also not working.
use configure(HttpSecurity http) method to secure your request-endpoints
http.csrf().disable()
.cors().and()
.authorizeRequests()
.antMatchers("/notsecured/**").permitAll()
.antMatchers("/secured/**").fullyAuthenticated()
.and().sessionManagement...
.and().formLogin()
...
use configure(WebSecurity web) method to ignore static resources like images, css,...

Multiple user details services for different endpoints

I am building a REST API using Spring and am currently authenticating all my requests using a custom user details service and this configuration code:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
I am also setting up a DaoAuthenticationProvider to use the my user details service and using that to configure global security.
Now, I want to provide an endpoint that (while still secured with HTTP basic authentication) uses a different user details service to check whether the user is allowed to access the given resource.
How do I use two different user details services for different endpoints?
One thing you can do is have two WebSecurityConfigurerAdapters:
#EnableWebSecurity
#Order(Ordered.HIGHEST_PRECEDENCE)
class FirstEndpointConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) {
http
.requestMatchers()
.antMatchers("/specialendpoint")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.userDetailsService(/* first of your userDetailsServices */);
}
}
#Configuration
class SecondEndpointConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) {
http // all other requests handled here
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.userDetailsService(/* second of your userDetailsServices */);
}
}
requestMatchers() exists for targeting springSecurityFilterChains to specific endpoints.
EDIT: Mahmoud Odeh makes a good point that if the user bases are the same, then you may not need multiple UserDetailsService instances. Instead, you can use one change that isolates your special endpoint by an authority on the user's account:
http
.authorizeRequests()
.antMatchers("/specialendpoint").hasAuthority("SPECIAL")
.anyRequest().authenticated()
.and()
.httpBasic();
Then, your single UserDetailsService would look up all users. It would include the SPECIAL GrantedAuthority in the UserDetails instance for users who have access to /specialendpoint.
I am trying to follow the solution given by M. Deinum but in my case it always goes to the same user service (v2userDetailsService) regardless of which URL is executed /v3/authorize/login or /v2/authorize/login. Here is my code:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration {
#Configuration
#Order(2)
public static class V2Configuration extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("v2userDetailsService")
private UserDetailsService v2userDetailsService;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
ShaPasswordEncoder passwordEncoder = new ShaPasswordEncoder(256);
auth
.userDetailsService(v2userDetailsService)
.passwordEncoder(passwordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and().csrf().disable().headers()
.frameOptions().disable().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/app").permitAll()
.antMatchers("/v2/authorize/login").permitAll()
.antMatchers("/v2/authorize/reLogin").permitAll()
.antMatchers("/v2/authorize/logout").permitAll();
}
}
#Configuration
#Order(1)
public static class V3Configuration extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("v3UserDetailsService")
private UserDetailsService v3UserDetailsService;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
ShaPasswordEncoder passwordEncoder = new ShaPasswordEncoder(256);
auth
.userDetailsService(v3UserDetailsService)
.passwordEncoder(passwordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and().csrf().disable().headers()
.frameOptions().disable().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/v3/authorize/login").permitAll()
.antMatchers("/v3/authorize/reLogin").permitAll()
.antMatchers("/v3/authorize/logout").permitAll();
}
}
}

Spring boot and spring security multiple login pages

#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Configuration
#Order(1)
public static class App1ConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/my/**", "/account/**").access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
.and().formLogin().loginPage("/login");
}
}
#Configuration
#Order(2)
public static class App2ConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.and().formLogin().loginPage("/adminlogin");
}
}
}
This is supposed be two different login forms. My problem is that the one with the highest order /adminlogin is not displayed. I have idea why? Please help. The code is from Spring boot - how to configure multiple login pages?
Following Sofia's suggestion I tried this:
#Configuration
#Order(2)
public static class UserConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(new AntPathRequestMatcher("/my/**"))
.csrf().disable()
.authorizeRequests().antMatchers("/my/**").access("hasRole('ROLE_USER')")
.and().formLogin().loginPage("/login");
}
}
#Configuration
#Order(1)
public static class AdminConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(new AntPathRequestMatcher("/admin/**"))
.csrf().disable()
.authorizeRequests().antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.and().formLogin().loginPage("/adminlogin");
}
}
But in both cases /login is called
I reckon that the reason why your admin login is not activating is because: first, it is NOT higher in priority.
#Order defines the sort order for an annotated component.
The value is optional and represents an order value as defined in the Ordered interface. Lower values have higher priority. The default value is Ordered.LOWEST_PRECEDENCE, indicating lowest priority (losing to any other specified order value).
Second, according to HttpSecurity's Javadoc:
A HttpSecurity is similar to Spring Security's XML element in the namespace configuration. It allows configuring web based security for specific http requests. By default it will be applied to all requests, but can be restricted using requestMatcher(RequestMatcher) or other similar methods.
So try restricting the HttpSecurity object to activate for your admin pages by first configuring the requestMatcher such that:
http
.requestMatcher(new AntPathRequestMatcher("/admin/**"))
.csrf().disable()
.authorizeRequests().antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.and().formLogin().loginPage("/adminlogin");
I solved it using request matcher:
#Configuration
#EnableWebSecurity
public class AllConfig extends WebSecurityConfigurerAdapter {
#Autowired
MyUserDeatailService myuserDetailsService;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider());
}
#Bean
public static BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(4);
}
#Bean
public AuthenticationProvider authProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(myuserDetailsService);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
#Bean
public static AuthenticationFailureHandler customAuthenticationFailureHandler() {
return new CustomAuthenticationFailureHandler();
}
#Configuration
#Order(1)
public static class AdminSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(new AntPathRequestMatcher("/admin/**"))
.csrf().disable()
.authorizeRequests()
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.and().formLogin()
.loginPage("/admin/adminlogin").permitAll().usernameParameter("username")
.passwordParameter("password").defaultSuccessUrl("/admin/AdminDashBoard")
.failureHandler(customAuthenticationFailureHandler()).and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/home").and()
.exceptionHandling().accessDeniedPage("/403");
}
}
#Configuration
#Order(2)
public static class UserSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(new AntPathRequestMatcher("/user/**"))
.csrf().disable()
.authorizeRequests()
.antMatchers("/user/**").access("hasRole('ROLE_USER')").and().formLogin()
.loginPage("/user/userlogin").permitAll().usernameParameter("username")
.passwordParameter("password").defaultSuccessUrl("/user/UserDashBoard")
.failureHandler(customAuthenticationFailureHandler()).and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/").and()
.exceptionHandling().accessDeniedPage("/403");
}
}
}

Spring Security OAuth2, which decides security?

I've been trying to implement a OAuth2 authentication server using the guides by Dave Syer with some inspiration from JHipster. But I can't figure out how it all works together.
It looks like the security setup using the WebSecurityConfigurerAdapter is overwritten when I use ResourceServerConfigurerAdapter.
#Configuration
#EnableResourceServer
public class OAuth2ResourceConfig extends ResourceServerConfigurerAdapter {
private TokenExtractor tokenExtractor = new BearerTokenExtractor();
#Override
public void configure(HttpSecurity http) throws Exception {
http
.addFilterAfter(contextClearer(), AbstractPreAuthenticatedProcessingFilter.class)
.authorizeRequests()
.anyRequest().authenticated().and().httpBasic();
}
private OncePerRequestFilter contextClearer() {
return new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (tokenExtractor.extract(request) == null) {
SecurityContextHolder.clearContext();
}
filterChain.doFilter(request, response);
}
};
}
#Component
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
private final AuthenticationManager authenticationManager;
#Autowired
public CustomWebSecurityConfigurerAdapter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.parentAuthenticationManager(authenticationManager);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login").permitAll()
.and()
.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.and()
.requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests().anyRequest().authenticated();
}
}
This is code taken from a few different examples, so they might not mix that well. But I can't find a good documentation/example list for OAuth2 (unlike Spring Boot which has a awesome documentation), so I'm having problems understanding how thye all fit together.
If I don't add the loginForm to the ResourceServerConfigurerAdapter, it will just give me unauthorized. But I defined it in the WebSecurityConfigurererAdapter as permitAll().
This is the AuthorizationServerConfigurerAdapter:
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("acme")
.secret("acmesecret")
.authorizedGrantTypes("authorization_code", "refresh_token",
"password").scopes("openid");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).accessTokenConverter(jwtAccessTokenConverter);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
}
Anything I'm doing wrong? Do I have to setup all the security within the ResourceServerConfigurerAdapter? Do I even need the WebSecurityConfigurerAdapter anymore?
If anyone know any guides, tutorials, blogs or anything alike that might help me wrap my head around how this works, that would be greatly appreciated.
Kind regards, Kenneth.
You need a WebSecurityConfigurerAdapter to secure the /authorize endpoint and to provide a way for users to authenticate. A Spring Boot application would do that for you (by adding its own WebSecurityConfigurerAdapter with HTTP basic auth). It creates a filter chain with order=0 by default, and protects all resources unless you provide a request matcher. The #EnableResourceServer does something similar, but the filter chain it adds is at order=3 by default. WebSecurityConfigurerAdapter has an #Order(100) annotation. So first the ResourceServer will be checked (authentication) and then your checks in your enxtension of WebSecurityConfigureAdapter will be checked.
Your configuration looks sane (the login chain takes precedence, but only matches a small set of requests).

Resources