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()
Related
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 have this class where I am trying to configure WebSecurityConfigurerAdapter. This code gives me permission to / , /home and /signup without needing to get authenticated.
My question is how to implement ErrorController while using HttpSecurity
Bacially if I get the This application has no explicit mapping for /error i dont want spring boot to prompt the login page first then to display the ErrorController. I want just to execetue ErrorController
public class WebConfigAdapter extends WebSecurityConfigurerAdapter {
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests()
.antMatchers("/","/home","/signup").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
httpSecurity.csrf().disable();
}
}
My ErrorController
#Controller
public class ErrorControllerH implements ErrorController {
#Override
#RequestMapping("/error")
public String getErrorPath() {
return "redirect:/";
}
}
What did you do by using antmatchers().permitall? You could imagine as a whitelist so everybody can access declared paths of your endpoint without getting in touch with Spring Security. So any user cann access this paths without authentication.
.antMatchers("/","/home","/signup", "/error").permitAll()
should trigger your ErrorController without getting HTTP status code 401 unauthorized. I try this out with #RestController and it works fine...
I have a Spring boot application with Spring security.
My problem is similar to this one, but in my case I want to redirect the user to the login page if he's not authenticated when he tries to access any page of the application.
The following image shows the architecture of the application:
My config class looks like this:
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").hasAnyRole("USER")
.and().formLogin().loginPage("/login").permitAll()
.and().authorizeRequests().antMatchers("/resources/**").permitAll().anyRequest().permitAll();
}
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
}
With this configuration, no resource will be loaded.
How can I configure my project to redirect the user to the login page if he's not authenticated and at the same time having my resources folder loaded?
plz checkout configure method
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/login*").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginPage("/login");
}
and implements WebMvcConfigurer Class like below
#Configuration
#EnableWebMvc
public class WebMvcConfiguration implements WebMvcConfigurer {
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
}
addResourceHandlers means find resources in /static.
Spring security is not allowing your css when a "GET" request to it is made allow it by changing the following line to the next line
this line = .antMatchers("/*.js").permitAll()
this line = .antMatchers("/*.js", "/*.css").permitAll()
Update your method by using authenticated() like below.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login*").
.antMatchers("/resources/**").permitAll()
.antMatchers("/*.js").permitAll()
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin();
}
Refer this article
I had this problem in my login page before authentication, I found it and resolved my problem by overrode this method in SecurityConfig
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/resources/**");
}
afterward login page knew .js and .css
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 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;
}