I have s SpringBoot application with freemarker templates.
MvcConfig:
#Configuration
public class MvcConfig implements WebMvcConfigurer {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
}
WebSecurityConfig:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public PasswordEncoder getPaswwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/user")
.and()
.rememberMe()
.and()
.logout().permitAll();
}
Login controller:
#Controller
#RequestMapping
public class LogInController {
#GetMapping("/")
public String greeting(Map<String, Object> model) {
return "redirect:/login";
}
}
When I ran my application and go to http://localhost:8080/login it returns an exception:
javax.servlet.ServletException: Circular view path [login]: would dispatch back to the current handler URL [/login] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
You're doing a recursive action~
When you try to access http://localhost:8080/login
your endpoint implementation redirects you to the same place
Acessing localhost:8090/login => redirect:/login
then try to access the same url
Acessing localhost:8090/login => redirect:/login
Related
I'm using spring boot, my CSS resources are located in .../src/main/resources/static and i have WebSecurityConfig class:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/registration", "/static/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService)
.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
}
and MvcConfig class:
#Configuration
public class MvcConfig implements WebMvcConfigurer {
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
}
When i logged in css resources are activated.
I have access to my templates when i'm not logged in.
Why CSS resources blocking for non authorized request?
Sorry for my English. I'll very grateful for your help!
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/control").hasRole("ADMIN")
.antMatchers("/register", "/login_auth", "/login").permitAll()
.antMatchers("/img/*.jpg", "/*.js", "/*.css").permitAll()...
i did this to access the files in the root static folder.
if you got em under folders do this instead:
.antMatchers("/img/**", "/js/**", "/css/**").permitAll()
I have the below configuration where i need to configure HTTPBasic authentication for /api/v1/** endpoints and i want to configure form authentication for /users/ url pattern. When i run with the below configuration, the configuration for web requests is working correctly but the configuration for API is not working. No security is being applied. Where am I going wrong?
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Order(1)
#Configuration
public static class MVCSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
public BCryptPasswordEncoder getBCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
antMatcher("/users/**")
.csrf()
.and()
.authorizeRequests()
.antMatchers(
"/resources/**", "/users/register", "/users/signup", "/users/confirm", "/users/user-action", "/users/reset-password", "/confirm", "/webjars/**")
.permitAll()
.antMatchers("/users/**")
.hasRole("USER")
.anyRequest()
.authenticated()
.and()
.formLogin().loginPage("/login").usernameParameter("username").passwordParameter("password");
http
.authorizeRequests()
.antMatchers("/api/v1/users/**")
.hasRole("USER")
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
}
I have put your code to work with this configuration bellow:
#EnableWebSecurity
public class SecurityConfiguration {
public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/v1/users/**")
.authorizeRequests().anyRequest()
.hasRole("USER").and().httpBasic();
}
}
#Configuration
#Order(2)
public class MVCSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().and().authorizeRequests()
.antMatchers("/resources/**", "/users/register", "/users/signup", "/users/confirm",
"/users/user-action", "/users/reset-password", "/confirm", "/webjars/**").permitAll()
.antMatchers("/users/**").hasRole("USER")
.and()
.formLogin().usernameParameter("username").passwordParameter("password");
}
}
}
View docs for Spring Security and sample code here.
So I have a two login pages. One for Customer and one for AM. I configured 2 login pages in my WebSecurityConfig class. When I try to login in AM using an admin account it works but when I try to login in Customer using a user account the loginProcessingUrl can't be found.
In my WebSecurityConfig class:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig
{
#Autowired
MyDBAuthenticationService myDBAuthenticationService;
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(myDBAuthenticationService);
}
#Configuration
#Order(1)
public static class WebConfigurationAdapter1 extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests().antMatchers("/am/**").access("hasRole('ROLE_AM')")
.and()
.exceptionHandling()
.accessDeniedPage("/403")
.and()
.formLogin()
.loginPage("/amLogin")
.loginProcessingUrl("/am/postLogin")
.defaultSuccessUrl("/amChatPage")
.failureUrl("/amLogin?error")
.and().logout().logoutUrl("/amLogout").logoutSuccessUrl("/amLogoutSuccessful")
.deleteCookies("JSESSIONID")
.and().csrf().disable();
System.out.println("1st Configurer");
}
}
#Configuration
#Order(2)
public static class WebConfigurationAdapter2 extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests().antMatchers("/customer/**").access("hasRole('ROLE_CUSTOMER')")
.and()
.exceptionHandling()
.accessDeniedPage("/403")
.and()
.formLogin()
.loginPage("/customerLogin")
.loginProcessingUrl("/customer/postLogin")
.defaultSuccessUrl("/customerChatPage")
.failureUrl("/customerLogin?error")
.and().logout().logoutUrl("/customerLogout").logoutSuccessUrl("/customerLogoutSuccessful")
.and().csrf().disable();
System.out.println("2nd Configurer");
}
}
}
Here's my SpringWebAppInitializer class:
#Configuration
public class SpringWebAppInitializer implements WebApplicationInitializer{
#Override
public void onStartup(ServletContext sc) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(ApplicationContextConfig.class);
ServletRegistration.Dynamic dispatcher = sc.addServlet("dispatcher", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
Things that I have done so far are:
Putting sout() in each static class to know if it gets run. Both did display the sout().
Change name of both processing url and make it unique. Also change in my jsp file.
Spring boot - how to configure multiple login pages?
Here is the result when I login as AM:
Result in netbeans. It enters the Controller but says "null"
Here is the result when I login as Customer:
Since there is no differentiation pattern between both http configuration Spring Security is taking the first one login as default, that is the reason why only admin login works, because it is part of the web security adapter declared as Order(1).
In order to separate both configuration properly it is necessary to define a pattern separation with .antMatcher.
here is one small example to give you an idea of how define both configuration
For admins (see the .antMatcher definition it forces to apply the http configuration only to admin/** urls.
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.antMatcher("/admin/**").authorizeRequests().anyRequest().authenticated().anyRequest().hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/adminLogin")
.loginProcessingUrl("/admin/postLogin")
.defaultSuccessUrl("/admin/home")
.failureUrl("/adminLogin?error")
.and().logout().logoutUrl("/admin/logout").logoutSuccessUrl("/home")
.and()
.csrf().disable();
}
For customers (see the .antMatcher definition it forces to apply the http configuration only to customer/** urls.
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.antMatcher("/customer/**").authorizeRequests().anyRequest().authenticated().anyRequest().hasRole("USER")
.and()
.formLogin()
.loginPage("/customerLogin")
.loginProcessingUrl("/customer/postLogin")
.defaultSuccessUrl("/customer/home")
.failureUrl("/customerLogin?error")
.and()
.logout().logoutUrl("/customer/logout").logoutSuccessUrl("/home")
.and()
.csrf().disable();
}
There are other examples here: Example two login pages and visit the spring security documentation Multiple Http Security
Hope this information helps you.
According to the new configuration you have to do some changes, please review the following configuration, and compare with yours and you will see what is the difference (antMatcher is different of antMatchers)
#Configuration
#EnableWebSecurity
public class WebSecurityConfig
{
#Autowired
MyDBAuthenticationService myDBAuthenticationService;
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(myDBAuthenticationService);
}
#Configuration
#Order(1)
public static class WebConfigurationAdapter1 extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
//.authorizeRequests().antMatchers("/am/**").access("hasRole('ROLE_AM')")
.antMatcher("/am/**").authorizeRequests().anyRequest().hasRole("AM")
.and()
.exceptionHandling()
.accessDeniedPage("/403")
.and()
.formLogin()
.loginPage("/amLogin")
.loginProcessingUrl("/am/postLogin")
.defaultSuccessUrl("/am/chatPage")
.failureUrl("/amLogin?error")
.and().logout().logoutUrl("/am/logout").logoutSuccessUrl("/amLogoutSuccessful")
.deleteCookies("JSESSIONID")
.and().csrf().disable();
System.out.println("1st Configurer");
}
}
#Configuration
#Order(2)
public static class WebConfigurationAdapter2 extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
//.authorizeRequests().antMatchers("/customer/**").access("hasRole('ROLE_CUSTOMER')")
.antMatcher("/admin/**").authorizeRequests().anyRequest().hasRole("CUSTOMER")
.and()
.exceptionHandling()
.accessDeniedPage("/403")
.and()
.formLogin()
.loginPage("/customerLogin")
.loginProcessingUrl("/customer/postLogin")
.defaultSuccessUrl("/customer/chatPage")
.failureUrl("/customerLogin?error")
.and().logout().logoutUrl("/customer/logout").logoutSuccessUrl("/customerLogoutSuccessful")
.and().csrf().disable();
System.out.println("2nd Configurer");
}
}
}
And finally remember the controller, you should have the following RequestMapping definition at least
#RequestMapping("/adminLogin"), #RequestMapping("/customerLogin"), #RequestMapping("/am/chatPage"), #RequestMapping("/customer/chatPage")
In the following class that extends WebSecurityConfigurerAdapter i've overwritten the configure(HttpSecurity) method.
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
public void configureAuth(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("fabio")
.password("123")
.roles("ADMIN")
.and()
.withUser("joe")
.password("123")
.roles("GUEST");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/post/list").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.logout();
}
}
With this i should be able to get to localhost:8080/post/list page without having to commit to a user login since it has the .permitAll() , but when i try to getting into it it always prompts the login page before, only after i enter the previous credentials i'm able to view it. How can i fix this ?
controller class
#RestController
#RequestMapping("/post")
public class HomeController {
#Secured("ROLE_GUEST")
#RequestMapping("/list")
public String list(){
return "list...";
}
#Secured("ROLE_USER")
#RequestMapping("/drafts")
public String drafts(){
return "drafts...";
}
#Secured({"ROLE_ADMIN","ROLE_USER"})
#RequestMapping("/add")
public String add(){
return "adding...";
}
}
According to the #RequestMapping definition there is a conflict because it is secured by annotation #Secured("ROLE_GUEST") but also you need to access it with .permitAll() configuration.
Option 1: Just remove the #Secured("ROLE_GUEST") in order to let .permitAll() do the work.
Option 2: use #Secured("ROLE_ANONYMOUS") on the #RequestMapping("/list") instead of #Secured("ROLE_GUEST"). You can see the definition of ROLE_ANONYMOUS in the Spring Documentation
It will depends on the path value after /post/list. Please see the following examples of how to define antMatchers depending on the path value.
localhost:8080/post/list = .antMatchers( "/post/list").permitAll()
localhost:8080/post/list/stuff = .antMatchers( "/post/list/**").permitAll()
localhost:8080/post/list, localhost:8080/post/list1234
= .antMatchers( "/post/list**").permitAll()
For more information visit the AnthPathMatcher documentation and HttpSecurity
I've got a problem with my Spring Boot application in which im trying to implement dynamically configured multiple login pages.
In the database are stored prefixes for pages and I'm trying to get multiple paths for separate login pages.
I'm trying to do it in the following SecurityConfig class
(global.getPath() returns a plain prefix String):
#Configuration
#EnableGlobalMethodSecurity( securedEnabled = true )
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private GlobalSettingsService globalSettingsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
List<GlobalSettings> globals = globalSettingsService.findAll();
http
.authorizeRequests()
.antMatchers("/css/**", "/index").permitAll()
.antMatchers("/js/**", "/").permitAll()
.antMatchers("/fonts/**", "/img/**").permitAll();
for (GlobalSettings global : globals) {
http.authorizeRequests()
.antMatchers("/"+global.getPath()+"/**").permitAll()
.antMatchers("/"+global.getPath()+"/admin/**").hasAnyRole("USER","ADMIN")
.and()
.formLogin().loginProcessingUrl("/"+global.getPath()+"/login")
.loginPage("/"+global.getPath()+"/login").permitAll()
.defaultSuccessUrl("/"+global.getPath()+"/admin")
.failureUrl("/"+global.getPath()+"/login?error").permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/"+global.getPath()+"/login?logout"))
.logoutSuccessUrl("/"+global.getPath()+"/login?logout")
.permitAll();
}
}
}
Login forms views are configured in another class (WebConfig) which extends WebMvcConfigureAdapter:
for (GlobalSettings global : globals) {
registry.addViewController("/"+global.getPath()+"/login").setViewName(global.getPath()+"-/login-form");
}
I've got 3 prefixes to configure and the last one configured in the loop is working correctly (propably with the highest order?) - I can log in and out without any troubles.
So the configuration works only for the last prefix which is called in the loop. Other prefixes are rendering proper login forms but returning 405 code after login attempt with following message:
o.s.web.servlet.PageNotFound: Request method 'POST' not supported
So I made custom POST method definition in one of my controllers:
#RequestMapping(value = "/{path}/login", method = RequestMethod.POST)
public ModelAndView getLoginPage(#PathVariable("path") String path, #RequestParam Optional<String> error) {
return new ModelAndView(path+"-front/login-form", "error", error);
}
But unfortunately it's not being recognized, still getting 'Method not supported' message, while the login request path is correct.
Besides, the paths are mapping correctly during application initialization so I don't know where the problem is.
I tried many ways such as adding hidden _csrf field to the login form and still getting nothing.
Is it possible to do it avoiding prefixes hardcoding?
Is it related with #Order annotation or something like this?
Thanks for your time
I resolved it by rebuilding SecurityConfig class making separate WebSecurityConfigurerAdapters for each of my prefix:
#Configuration
#EnableGlobalMethodSecurity( securedEnabled = true )
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Autowired
private GlobalSettingsService globalSettingsService;
#Configuration
#Order(1)
public static class FirstPrefixWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/app1/**")
.authorizeRequests()
.antMatchers("/app1/**").permitAll()
.antMatchers("/app1/login").permitAll()
.antMatchers("/app1/admin/**")
.hasAnyRole("ADMIN","USER")
.and()
.formLogin().loginProcessingUrl("/app1/login")
.loginPage("/app1/login").permitAll()
.defaultSuccessUrl("/app1/admin")
.failureUrl("/app1/login?error").permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/app1/login?logout"))
.logoutSuccessUrl("/app1/login?logout")
.permitAll();
}
}
#Configuration
#Order(2)
public static class SecondPrefixWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/app2/**")
.authorizeRequests()
.antMatchers("/app2/**").permitAll()
.antMatchers("/app2/login").permitAll()
.antMatchers("/app2/admin/**")
.hasAnyRole("ADMIN","USER")
.and()
.formLogin().loginProcessingUrl("/app2/login")
.loginPage("/app2/login").permitAll()
.defaultSuccessUrl("/app2/admin")
.failureUrl("/app2/login?error").permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/app2/login?logout"))
.logoutSuccessUrl("/app2/login?logout")
.permitAll();
}
}
#Configuration
#Order(3)
public static class ThirdPrefixWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/app3/**")
.authorizeRequests()
.antMatchers("/app3/**").permitAll()
.antMatchers("/app3/login").permitAll()
.antMatchers("/app3/admin/**")
.hasAnyRole("ADMIN","USER")
.and()
.formLogin().loginProcessingUrl("/app3/login")
.loginPage("/app3/login").permitAll()
.defaultSuccessUrl("/app3/admin")
.failureUrl("/app3/login?error").permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/app3/login?logout"))
.logoutSuccessUrl("/app3/login?logout")
.permitAll();
}
}
}
It isn't a dynamic solution but it's alright for now.