I have followed the advice from the official documentation on how to configure two separate HttpSecurity instances:
#Configuration
#EnableWebSecurity
public class SoWebSecurityConfig
{
#Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(username -> {
log.info("\n\n\n ********* authenticating {} ************************************\n\n\n", username);
return new User(username, "", asList(new SimpleGrantedAuthority("TV")));
});
}
#Configuration
#Order(1)
public static class SwiperSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception { configureHttpSec(http, "/swiper"); }
}
#Configuration
#Order(2)
public static class TvSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception { configureHttpSec(http, "/tv"); }
}
static HttpSecurity configureHttpSec(HttpSecurity http, String urlBase) throws Exception {
http .csrf().disable()
.exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint())
.and() .authorizeRequests().antMatchers(urlBase+"/**").authenticated()
.and() .httpBasic()
.and() .logout().logoutUrl(urlBase+"/logout").logoutSuccessHandler((req,resp,auth) -> {})
;
return http;
}
}
In the logs I do see two filter chains being created:
2014-06-30 12:44:22 main INFO o.s.s.w.DefaultSecurityFilterChain - Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher#1, [org.springframework.security.web.context.request.as
ync.WebAsyncManagerIntegrationFilter#806996, org.springframework.security.web.context.SecurityContextPersistenceFilter#1937eaff, org.springframework.security.web.header.HeaderWriterFilter#71e4b308, org.springfr
amework.security.web.authentication.logout.LogoutFilter#1d1cbd0f, org.springframework.security.web.authentication.www.BasicAuthenticationFilter#9b9a327, org.springframework.security.web.savedrequest.RequestCach
eAwareFilter#4993febc, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#67064bdc, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#78b612c6, org.s
pringframework.security.web.session.SessionManagementFilter#6d11ceef, org.springframework.security.web.access.ExceptionTranslationFilter#6e7c351d, org.springframework.security.web.access.intercept.FilterSecurit
yInterceptor#571a01f9]
2014-06-30 12:44:22 main INFO o.s.s.w.DefaultSecurityFilterChain - Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher#1, [org.springframework.security.web.context.request.as
ync.WebAsyncManagerIntegrationFilter#30c1da48, org.springframework.security.web.context.SecurityContextPersistenceFilter#427ae189, org.springframework.security.web.header.HeaderWriterFilter#4784efd9, org.spring
framework.security.web.authentication.logout.LogoutFilter#187e5235, org.springframework.security.web.authentication.www.BasicAuthenticationFilter#514de325, org.springframework.security.web.savedrequest.RequestC
acheAwareFilter#16a9eb2e, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#76332405, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#43a65cd8, or
g.springframework.security.web.session.SessionManagementFilter#3fba233d, org.springframework.security.web.access.ExceptionTranslationFilter#376c7d7d, org.springframework.security.web.access.intercept.FilterSecu
rityInterceptor#3b48e183]
but only the one I designate with Order(1) will actually get used; the URLs matching the other one will not get authenticated.
I have also tried following the docs more closely, using anyRequest() instead of ant matchers for the #Order(2) configuration, but the result was the same.
What are my options to get around this problem?
I am using Spring 4.0.5, Spring Security 3.2.4.
You have failed to follow the documentation in one crucial aspect. You have
http.authorizeRequests().antMatchers(urlBase+"/**").authenticated()
which means that you register this HttpSecurity as a global security module, which applies to all URLs, but only requires authentication on those selected with the Ant matcher. When you do this twice, you end up with two chained global security modules, so naturally only the first one will be responsible for all URLs.
The documentation instead advises this:
http.antMatcher(urlBase+"/**").authorizeRequests().anyRequest().authenticated()
which means that the Ant matcher will used to select which URL this security module is responsible for, and bypass it for all others. This way the second module in line gets its chance when appropriate.
So all you need to do is slightly adjust your static configurer method to the following:
static HttpSecurity configureHttpSec(HttpSecurity http, String urlBase) throws Exception {
http .csrf().disable()
.exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint())
.and() .antMatchers(urlBase+"/**").authorizeRequests().anyRequest().authenticated()
.and() .httpBasic()
.and() .logout().logoutUrl(urlBase+"/logout").logoutSuccessHandler((req,resp,auth) -> {})
;
return http;
}
Related
I'm trying to make a web application that uses:
SpringBoot,
Mysql,
JDBC
, MVC, DAO
Thymeleaf,
IntelliJ
And I'm trying to figure out how Spring security works (which I'm having a lot of difficulty with).
My views are organized as follows:
resources(folder): - ________static(folder)
|____templates(folder):__________images(folder)
|___userOnly(folder):_____header.html
| |__help.html
| |__menu.html
| |__newDocForm.html
| |__profil.html
|
|__firstPage.html
|__header.html
|__home.html
|__index.html
|__inscriptionForm.html
|__loginPage.html
I would like to do that unidentified users can access all views except those contained in "userOnly" and that my "loginPage" page is used as the login page.
If I understood correctly, I must create a class that inherits from "WebSecurityConfigurerAdapter".
What I have done.
And then configure "configure", which I can't do correctly :(
#Configuration
#EnableWebSecurity
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/userOnly/**").hasRole("USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/loginPage.html");
}
}
Sorry if my questions seems strange but english is not my first language
As of Spring-Boot 2.7 the use of WebSecurityConfigurerAdapter is deprecated. If you're using Spring-Boot 2.6 or older the other answers might suit you better.
To my best knowledge the recommended way for defining security config in Spring-Boot 2.7 is as follows:
#Configuration
public class WebSecurityConfig
{
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception
{
// #formatter:off
http.authorizeRequests()
.mvcMatchers("/userOnly/**").permitAll()
.anyRequest().permitAll();
http.formLogin()
.permitAll()
.loginPage("/loginPage.html");
http.logout()
.permitAll();
// #formatter:on
return http.build();
}
}
The use of web.ignoring() in the answer from voucher_wolves is, I believe, not recommended, instead one should add those cases to http.mvcMatcher().permitAll().
On a side note, I would personally recommend whitelisting the public pages and adding authentication to everything else, (for example a public folder). This way if you forget to add security to a link it's not public by default.
You need to tell Spring security what URL are public with something like this -
#Configuration
#EnableWebSecurity
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] PUBLIC_URLS = {"/public/*"};
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/userOnly/**").hasRole("USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/loginPage.html");
}
#Override
public void configure(WebSecurity web) {
List<RequestMatcher> matchers =
Arrays.asList(urls).stream().map(url -> new
AntPathRequestMatcher(url)).collect(Collectors.toList());
web.ignoring().requestMatchers(new OrRequestMatcher(matchers));
}
}
With OrRequestMatcher , you can create list of all URLs which you need to be public.
You can also use NegatedRequestMatcher to get all the private URL
RequestMatcher privateUrlMatcher = new
NegatedRequestMatcher(publicUrlMatcher);
I also suggest you to keep all public url under /src/main/resources/static/publicui and all private under /src/main/resources/static/privateui and have public permission for /publicui/*
try the following in your SecSecurityConfig class
#Configuration
#EnableAutoConfiguration
#EnableWebSecurity
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/users").authenticated()
.anyRequest().permitAll()
.and()
.formLogin()
.usernameParameter("email")
.defaultSuccessUrl("/lib/allBooks")
.permitAll()
.and()
.logout().logoutSuccessUrl("/lib").permitAll();
http
.csrf().disable();
}
}
Just modify the parameters set for your application. if you don't have login form yo can skip
.usernameParameter("email")
.defaultSuccessUrl("/lib/allBooks")
.permitAll()
I have multiple WebSecurityConfigurerAdapter's in my code, each resides in a different class.
We are migrating to spring-security 5.2, and as a result, we should remove #EnableResourceServer and replace it with oauth2RespourceServer DSL way.
First WebSecurityConfigurerAdapter is called CommonWebSecurityConfig, and it includes many ant-matchers that don't have any shared prefix:
#Configuration
#EnableWebSecurity
#ComponentScan
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CommonWebSecurityAutoConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
// .requestMatchers()
// .antMatchers("/* Should I add all (long) list of matchers here?*/")
// .and()
.authorizeRequests()
.antMatchers(GET, "/health").permitAll()
.antMatchers("/loggers/**").hasAuthority("scope1")
.antMatchers("/info/**").hasAuthority("scope2")
.antMatchers("/metrics/**").hasAuthority("scope3")
.antMatchers("/configurations/**").hasAuthority("scope4")
.antMatchers("/odata.svc/**").hasAuthority("scope5")
.antMatchers("/dataCenters/**").hasAuthority("scope6")
.antMatchers("/metadata/v1**").hasAuthority("scope7")
...
...
...
...
...
... // list goes on
...
.and()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(getJwtAuthoritiesConverter());
}
Second WebSecurityConfigurerAdapter is AccountsWebSecurityConfig meant to be the second to be validated against. I.e. if a request did not match any ant matcher in CommonWebSecurityConfig, it should be validated against AccountsWebSecurityConfig
#Configuration
#EnableWebSecurity
#ComponentScan(basePackages = "com.xyz..**.security")
#Order(AFTER_COMMON)
public class AccountsWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
// .requestMatchers()
// .antMatchers("/accounts/**")
// .and()
.authorizeRequests()
.mvcMatchers("/accounts/v1/go").hasAuthority("scope33")
...
... // list goes on
...
.and()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(getJwtAuthoritiesConverter());
}
My questions:
Requests are not being validated against AccountsWebSecurityConfig: any request that starts with "accounts" is granted access!
When I uncomment the 3 lines at the beginning of configure() in both classes, it start to work.
This is a different behavior to what we had prior to spring-security 5.2: we did not have to add requestMatchers to make it work. Is this a new requirement?
If yes, then do I have to add requestMatchers to all antMatchers? what if I have so many of them that don't share a prefix, as in CommonWebSecurityConfig?
Do I have to add and().oauth2ResourceServer() to both of them?
I am trying to develop Spring Security project with JWT.
I want access Login api with out Spring Security (without JWT token). But with below configuration, every time (for login api as well) it is checking for JWT token giving me 403 error.
Below is my WebSecurityConfig.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthFilter jwtAuthFilter;
#Autowired
private TokenAuthenticationService jwtAuthenticationProvider;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(jwtAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().ignoringAntMatchers("/api/v1/login");
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/api/v1/login")
.permitAll()
.and()
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
}
}
Thanks in advance
For login path configuration something like this can be used:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin()
.usernameParameter("username") // default is username
.passwordParameter("password") // default is password
.loginPage("/authentication/login") // default is /login with an HTTP get
.failureUrl("/authentication/login?failed") // default is /login?error
.loginProcessingUrl("/authentication/login/process"); // default is /login
// with an HTTP
// post
}
If some paths need to be ignored configure(WebSecurity web) can be overridden:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/v1/somepath").antMatchers("/static/**");
}
There is filter class named JwtAuthFilter that is being executed before every service you call.
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
this code provides to be executed filter before every request, but its okay, you have to see this FilterClass there must be some check if token doesnt exist filter class must be returned and request will directly go to the login service. if you can show that Filter class and I will help you.
My spring boot application has an Application class. When I run it (as an application), it launches itself within an embedded servlet container (Tomcat, in my case). Somehow (through Application's #annotations, I suppose), WebSecurityConfig (extending WebSecurityConfigurerAdapter) in the same package is loaded.
WebSecurityConfig contains two important blocks of configuration information:
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
#EnableGlobalMethodSecurity(prePostEnabled = true) // enables method-level role-checking
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userSearchBase("CN=Users,DC=some,DC=domain,DC=com")
.userSearchFilter("(sAMAccountName={0})")
.groupSearchBase("OU=Groups,DC=some,DC=domain,DC=com")
.groupSearchFilter("(member={0})")
.contextSource()
.managerDn("cn=ad-bind,cn=users,dc=some,dc=domain,dc=com")
.managerPassword("APASSWORD!")
.url("ldaps://some.domain.com:636");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("***************** WebSecurityConfig.configure *************************");
http.csrf().disable();
http
.headers()
.frameOptions()
.disable();
http
.authorizeRequests()
.antMatchers("/resources/images/*", "/me", "/products", "/product/**", "/offerings", "/offering/**", "/client/**")
.permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").defaultSuccessUrl("/me")
.permitAll()
.and()
.logout()
.permitAll();
http.logout().logoutSuccessUrl("/me");
}
}
configureGlobal() contains the configuration for our internal LDAP system and it works just fine.
configure() specifies which URLs are public, which are only to be shown to logged-in users and which relative URLs to send users to as they log in.
Now I'm into integration testing and have written some methods to test controllers that do not require authentication. Those tests work as expected. The Application class fires up and the tests execute against it.
But now I want to test controller methods that DO require authentication. The way I think this is accomplished is by telling the test class to fire up an alternative Application class (TestApplication, in my case) and WebSecurityConfig that creates dummy users:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestApplication.class) // fires up with TestApplication.class instead of Application.class
#WebAppConfiguration
public class ProductControllerTests {
// test methods here, this time with username/password included
}
#Configuration
#EnableAutoConfiguration
public class TestApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(applicationClass, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(applicationClass);
}
private static Class<TestApplication> applicationClass = TestApplication.class;
}
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("testuser").password("userpass").roles("USER");
auth.inMemoryAuthentication().withUser("testadmin").password("adminpass").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("***************** WebSecurityConfig.configure *************************");
http.csrf().disable();
http
.headers()
.frameOptions()
.disable();
http
.authorizeRequests()
.antMatchers("/resources/images/*", "/me", "/products", "/product/**", "/offerings", "/offering/**", "/client/**")
.permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").defaultSuccessUrl("/me")
.permitAll()
.and()
.logout()
.permitAll();
http.logout().logoutSuccessUrl("/me");
}
}
So my question is: When I execute the unit test class, I believe TestApplication is firing. However, it is NOT picking up the alternative WebSecurityConfig class and its auth.inMemoryAuthentication() test users. How do I force my application to use one WebSecurityConfig when running the application normally, but a different WebSecurityConfig when running the unit tests?
You can configure your TestApplication to include just the beans that you would like to test. In other words, make sure that your WebSecurityConfig is not part of the test configuration. If you read the javadoc of #SpringBootApplication you will notice that it is a composite annotation that consists of (among others) the #ComponentScan annotation. Consequently your Application and your TestApplication will perform a recursive scan from the package in which the class is located. The Spring reference docs has a specific chapter about Using filters to customize scanning.
Alternatively, if you are using Spring Security version 4 or greater you may find the additions of #WithMockUser and #WithUserDetails interesting.
In your security configuration class, add #Profile annotation to disable in unit test profile. like:
#Configuration
#Profile("!" + Constants.SPRING_PROFILE_UNITTEST)
public class WebSecurityConfig { ....}
And let your another security config for test just in test dir.
I created a Spring Security configuration class for Spring-Boot. My login page has resources css, js and ico files. The resources are getting denied for security reasons and redirected to login page each time. Why does EnableWebMVCSecurity not add the Classpath resource location. After changing the code as in the second snippet the I Classpath resource location is added. dont understand what I am missing for the resources in the first code snippet.
#Configuration
/*
* Enable Spring Security’s web security support and provide the Spring MVC integration
* It also extends WebSecurityConfigurerAdapter and overrides a couple of its methods to set some specifics of the web security configuration.
*/
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* The configure(HttpSecurity) method defines with URL paths should be
* secured and which should not.
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated();
// There is a custom "/login" page specified by loginPage(), and everyone
// is allowed to view it.
http
.formLogin()
.loginPage("/login.html")
.permitAll()
.and()
.logout()
.permitAll().logoutSuccessUrl("/login.html");
}
#Configuration
protected static class AuthenticationConfiguration extends
GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
// As for the configure(AuthenticationManagerBuilder) method, it sets up
// an in-memory user store with a single user. That user is given a
// username of "user", a password of "password", and a role of "USER".
auth
.inMemoryAuthentication()
.withUser("user#domain.com").password("password").roles("USER");
}
}
I got this working by changing the code to
#Configuration
/*
* Enable Spring Security’s web security support and provide the Spring MVC integration
* It also extends WebSecurityConfigurerAdapter and overrides a couple of its methods to set some specifics of the web security configuration.
*/
public class WebSecurityConfig{
#Bean
public ApplicationSecurity applicationSecurity() {
return new ApplicationSecurity();
}
#Bean
public AuthenticationSecurity authenticationSecurity() {
return new AuthenticationSecurity();
}
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated();
http
.formLogin()
.loginPage("/login.html")
.permitAll()
.and()
.logout()
.permitAll().logoutSuccessUrl("/login.html");
}
}
#Order(Ordered.HIGHEST_PRECEDENCE + 10)
protected static class AuthenticationSecurity extends
GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user#domain.com").password("password").roles("USER");
}
}
}
After changing the code I noticed that the Ignore paths were added to the filter and I see the following in logs:
[ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: Ant [pattern='/css/**'], []
[ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: Ant [pattern='/js/**'], []
[ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: Ant [pattern='/images/**'], []
[ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: Ant [pattern='/**/favicon.ico'], []
[ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher#1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#4e3e0069, org.springframework.security.web.context.SecurityContextPersistenceFilter#3d2dd0cf, org.springframework.security.web.header.HeaderWriterFilter#33fc3b02, org.springframework.security.web.csrf.CsrfFilter#9b7a3ac, org.springframework.security.web.authentication.logout.LogoutFilter#267237ef, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#129495ef, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter#7db0a467, org.springframework.security.web.authentication.www.BasicAuthenticationFilter#764d1dbd, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#25a5268d, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#15c01d0c, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#37818a3b, org.springframework.security.web.session.SessionManagementFilter#3fe57e49, org.springframework.security.web.access.ExceptionTranslationFilter#4278af59, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#424bef91]
Per the docs you have disabled the spring boot autoconfig in the first example by using #EnableWebSecurity, so you would have to explicitly ignore all the static resources manually. In the second example you simply provide a WebSecurityConfigurer which is additive on top of the default autoconfig.
Create a Configuration file that extends WebSecurityConfigurerAdapter and annotate the class with #EnableWebSecurity
You can override methods like configure(HttpSecurity http) to add basic security like below
#Configuration
#EnableWebSecurity
public class AppWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().permitAll();
}
}
Add below method to by pass security for css and js in security config -
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/** **","/js/** **");
}