I want configure Spring Security to work with two different login pages for some pages
The goal is:
/admin should use a custom login page
/actuator should use the standard spring login page
all other (like /api or so) are permitted without login
My problem is:
My solution does not work.
All /admin pages and the /actuator page are accessable without login
Here is my SecurityConfig
#Configuration
#EnableWebSecurity
public class MySecurityConfig {
#Configuration
#Order(1)
public static class AdminAppConfiguration {
final AdminEnvironmentProperties adminEnvironmentProperties;
public AppConfiguration(AdminEnvironmentProperties adminEnvironmentProperties) {
this.adminEnvironmentProperties = adminEnvironmentProperties;
}
#Bean
public LogoutSuccessHandler logoutSuccessHandler() {
return new AdminLogoutSuccessHandler();
}
#Bean
public AuthenticationManager authManager(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.inMemoryAuthentication().withUser("Admin").password("changeme").authorities(this.adminEnvironmentProperties.getAllowGroup());
return authenticationManagerBuilder.build();
}
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.antMatcher("/admin*")
.authorizeRequests()
.anyRequest()
.hasAuthority(this.adminEnvironmentProperties.getAllowGroup())
.and()
.formLogin()
.loginPage("/admin/login")
.permitAll()
.and()
.logout()
.permitAll()
.logoutSuccessHandler(this.logoutSuccessHandler())
.and()
.csrf().disable();
return http.build();
}
}
#Configuration
#Order(2)
public static class OtherAppConfiguration {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/actuator").authenticated()
.anyRequest().permitAll()
.and().httpBasic();
return http.build();
}
}
}
How can i fix that, so that all pages behind /admin get the custom login page, /actuator gets the standard login page and all others (like /api/** or so) are permitted without login page
I found the solution by myself
My mistake was to use the method name securityFilterChain twice.
Changing the method names from securityFilterChain to admin and allOther did the trick
MY SOLUTION
#Configuration
#EnableWebSecurity
public class MySecurityConfig {
final AdminEnvironmentProperties adminEnvironmentProperties;
public AppConfiguration(AdminEnvironmentProperties adminEnvironmentProperties) {
this.adminEnvironmentProperties = adminEnvironmentProperties;
}
#Bean
public LogoutSuccessHandler logoutSuccessHandler() {
return new AdminLogoutSuccessHandler();
}
#Bean
public AuthenticationManager authManager(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.inMemoryAuthentication().withUser("Admin").password("changeme").authorities(this.adminEnvironmentProperties.getAllowGroup());
return authenticationManagerBuilder.build();
}
#Bean
#Order(1)
public SecurityFilterChain admin(HttpSecurity http) throws Exception {
http.csrf().disable()
.antMatcher("/admin/**")
.authorizeRequests()
.antMatchers("/admin/**").hasAuthority(this.adminEnvironmentProperties.getAllowGroup())
.and()
.formLogin()
.loginPage("/admin/login").permitAll()
.and()
.logout().permitAll()
.logoutSuccessHandler(this.logoutSuccessHandler());
return http.build();
}
#Bean
#Order(2)
public SecurityFilterChain allOther(HttpSecurity http) throws Exception {
http.csrf().disable()
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/actuator").authenticated()
.and().httpBasic();
return http.build();
}
}
I have checked many similar questions but could not solve my problem.
I have the following two httpsecurity for admin and user.
#EnableWebSecurity
public class SecurityConfiguration {
#Configuration
#Order(1)
public static class AdminSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
// #formatter:off
http
.antMatcher("/admin/**")
.authorizeRequests()
.antMatchers("/admin/**").hasAuthority("ROLE_ADMIN")
.and()
.formLogin()
.loginPage("/admin-login")
.loginProcessingUrl("/admin-login")
.failureUrl("/admin-login?error")
.defaultSuccessUrl("/admin/home")
.and()
.logout()
.logoutUrl("/admin-logout")
.logoutSuccessUrl("/admin-login?logout")
.permitAll()
.and()
.csrf().disable();
// #formatter:on
}
}
#Configuration
#Order(2)
public static class UserSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
// #formatter:off
http
.antMatcher("/user/home/**")
.authorizeRequests()
.antMatchers("/user/home/**").hasAuthority("ROLE_USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/user-login")
.loginProcessingUrl("/user-login")
.failureUrl("/user-login?error")
.defaultSuccessUrl("/user/home")
.and()
.logout()
.logoutUrl("/user-logout")
.logoutSuccessUrl("/user-login?logout")
.and()
.csrf().disable();
// #formatter:on
}
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public UserDetailsService userDetailsService() {
User.UserBuilder users = User.builder();
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(users.username("user").password(passwordEncoder().encode("user")).roles("USER").build());
manager.createUser(users.username("admin").password(passwordEncoder().encode("admin")).roles("USER", "ADMIN").build());
return manager;
}
}
Whenever I try to login to either admin or user side, I get the following error.
2021-03-11 16:46:31.357 WARN 1468 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
Any help is much appreciated.
Here is the repo to reproduce the problem.
https://github.com/venustulips/ss-2-login
hi how i can use this orders for my controller and restController ....
like -> order 1 for html view and order 2 for rest api
i want use it for webapp using rest and mvc in spring
Multiple Entry Points With Multiple HTTP Elements
i think i should using order in my controller class!
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/user/**").hasRole("EMPLOYEE")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/LoginPage")
.loginProcessingUrl("/authenticateTheUser")
.successHandler(customAuthenticationSuccessHandler)
.permitAll()
.and()
.logout().permitAll() `enter code here`
.and()
.exceptionHandling().accessDeniedPage("/access-denied");
}
}
#Configuration
#Order(2)
public class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(m.authenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/**").hasRole("EMPLOYEE")
.and()
.httpBasic()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
}
i work on this problem and find way for using spring rest api and spring mvc in single
project this is easy to use them in one project with out security
for spring rest security and spring mvc security with login page and rest basic auth registery in a project we should use httpBasic()
and for url use
http://username:password#localhost:8080/api/members/
#Configuration
#EnableWebSecurity
public class MultipleEntryPointsSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Autowired
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
// this is filter for mappings for api and mvc mappings
// http://username:password#localhost:8080/api/members/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").hasRole("EMPLOYEE")
.antMatchers("/leaders/**").hasRole("MANAGER")
.antMatchers("/systems/**").hasRole("ADMIN")
.antMatchers(HttpMethod.GET, "/api/**").hasRole("EMPLOYEE")
.and()
.httpBasic()
.and()
.formLogin()
.loginPage("/showMyLoginPage")
.loginProcessingUrl("/authenticateTheUser")
.successHandler(customAuthenticationSuccessHandler)
.permitAll()
.and()
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage("/access-denied");
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userService); //set the custom user details service
auth.setPasswordEncoder(passwordEncoder()); //set the password encoder - bcrypt
return auth;
}
}
I'm trying to establish concurrent session control with non-xml Spring Security, so a user can't log in if he is already logged in in another device. I used .sessionManagement() .maximumSessions(1) .maxSessionsPreventsLogin(true), but using Chrome and Firefox I still can log in concurrently.
I have tried configuring HttpSessionEventPublisher as instructed by another post, but I'm still able to log in concurrently.
This is my WebSecurityConfigurerAdapter :
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AccessDeniedHandler accessDeniedHandler;
#Autowired
UsuarioRepository usuarioRepository;
#Bean
public UserDetailsService mongoUserDetails() {
return new DinamicaUserDetailsService();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
UserDetailsService userDetailsService = mongoUserDetails();
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/",
"/home",
"/about",
"/registro",
"/session-error",
"/img/**",
"/img/*").permitAll()
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.antMatchers("/user/**").hasAnyRole("USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout")
.permitAll()
.invalidateHttpSession(true)
.and()
.sessionManagement()
.maximumSessions(1)
.expiredUrl("/session-error")
.maxSessionsPreventsLogin(true);
}
}
I expect it to show an error if I try to log in in Chrome while still logged in in Firefox, but the second concurrent login is successful.
While creating the session, session registry compare the the object of UserDetails to check if there is already a session for that principal.
Since you are using a custom UserDetails service DinamicaUserDetailsService, you should override hashcode and equals method to make sure they are matched for same user. For example you can compare user id or any other unique attribute of user.
#Override
public boolean equals(Object user) {
if(user == null) return false;
return (user.getId() == getId());
}
There are several references of multiple authentication providers in spring security, but no example in Java config could be located.
The following link gives the XML notation:
Multiple Authentication Providers in Spring Security
We need to authenticate using LDAP or DB
Below is our sample code:
#Configuration
#EnableWebSecurity
public class XSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationProvider authenticationProvider;
#Autowired
private AuthenticationProvider authenticationProviderDB;
#Override
#Order(1)
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
#Order(2)
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProviderDB);
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/scripts/**","/styles/**","/images/**","/error/**");
}
______
#Override
#Order(1)
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/","/logout","/time").permitAll()
.antMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/index")
.loginProcessingUrl("/perform_login")
.usernameParameter("email")
.passwordParameter("password")
.failureUrl("/index?failed=true")
.defaultSuccessUrl("/summary",true)
.permitAll()
.and()
.logout().logoutUrl("/logout")
.logoutSuccessUrl("/index?logout=true").permitAll()
.and()
.exceptionHandling().accessDeniedPage("/error403")
.and().authenticationProvider(authenticationProvider);
}
#Order(1)
protected void configureDB(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/","/logout").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/index")
.loginProcessingUrl("/perform_login")
.usernameParameter("email")
.passwordParameter("password")
.failureUrl("/index?failed=true")
.defaultSuccessUrl("/summary",true)
.permitAll()
.authenticationProvider(authenticationProviderDB)
//This line giving compilation error stating authenticationProvider is not available in formloginconfigurer
.and()
.logout().logoutUrl("/logout")
.logoutSuccessUrl("/index?logout=true").permitAll()
.and()
.exceptionHandling().accessDeniedPage("/error403");
}
}
May be this will help you :-
#Configuration
#EnableWebSecurity
#Profile("container")
public class XSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationProvider authenticationProvider;
#Autowired
private AuthenticationProvider authenticationProviderDB;
#Override
#Order(1)
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
#Order(2)
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProviderDB);
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/scripts/**","/styles/**","/images/**","/error/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/rest/**").authenticated()
.antMatchers("/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(new AuthenticationSuccessHandler() {
#Override
public void onAuthenticationSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication a) throws IOException, ServletException {
//To change body of generated methods,
response.setStatus(HttpServletResponse.SC_OK);
}
})
.failureHandler(new AuthenticationFailureHandler() {
#Override
public void onAuthenticationFailure(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException ae) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
})
.loginProcessingUrl("/access/login")
.and()
.logout()
.logoutUrl("/access/logout")
.logoutSuccessHandler(new LogoutSuccessHandler() {
#Override
public void onLogoutSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication a) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
}
})
.invalidateHttpSession(true)
.and()
.exceptionHandling()
.authenticationEntryPoint(new Http403ForbiddenEntryPoint())
.and()
.csrf()//Disabled CSRF protection
.disable();
}
}
In Spring Boot this worked for me:
Each Authentication provider is tested in order. If one passes, then its following Authentication providers are skipped
auth.userDetailsService(userDetailsService)...
then:
auth.ldapAuthentication()....
#EnableRedisHttpSession
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
//each Authentication provider is tested in order
//if one passes then its following Authentication providers are skipped
//DataBase Authentication
auth.userDetailsService(userDetailsService).passwordEncoder(passwordencoder());
LdapContextSource ldapContextSource = new LdapContextSource();
ldapContextSource.setUrl("ldap://192.168.XXX.XXX:389");
ldapContextSource.setBase("dc=companyname,dc=com");
ldapContextSource.setUserDn("cn=user,cn=testgroup,ou=Test,dc=companyname,dc=com");
ldapContextSource.setPassword("user1234");
ldapContextSource.afterPropertiesSet();
//LDAP Authentication
auth.ldapAuthentication()
//The {0} in the (uid={0}) will be replaced by the username entered in the form.
.userSearchBase("ou=Group")
.userSearchFilter("uid={0}")
//.userDnPatterns("uid={0},ou=people")//does the same thing
//Specifies where the search for Roles start
//.groupSearchBase("ou=mathematicians")
//in groups we search for member
//.groupSearchFilter("member={0}")
//.contextSource().ldif("classpath:test-server.ldif");
.contextSource(ldapContextSource);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/hello").access("hasRole('ROLE_ADMIN')")
.antMatchers("/index").fullyAuthenticated()
.antMatchers("/").fullyAuthenticated()
.antMatchers("/home").fullyAuthenticated()
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.usernameParameter("username").passwordParameter("password")
.and()
.logout()
.logoutSuccessUrl("/login?logout")
.permitAll()
.and()
.exceptionHandling()
.accessDeniedPage("/403")
.and()
.csrf()
.disable();
}
#Bean(name = "passwordEncoder")
public PasswordEncoder passwordencoder() {
return new BCryptPasswordEncoder();
}
}
This is a successful configuration which helps configure multiple authentication providers in java config.
Thanks a lot ojus for your inputs. It did help in nailing down the issue.
The key is to have
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
auth.authenticationProvider(authenticationProviderDB);
}
Full code below
#Configuration
#EnableWebSecurity
public class XSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private LDAPAuthenticationProvider authenticationProvider;
#Autowired
private DBAuthenticationProvider authenticationProviderDB;
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/scripts/**","/styles/**","/images/**","/error/**");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
auth.authenticationProvider(authenticationProviderDB);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/","/logout").permitAll()
.antMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/index")
.loginProcessingUrl("/perform_login")
.usernameParameter("user")
.passwordParameter("password")
.failureUrl("/index?failed=true")
.defaultSuccessUrl("/test",true)
.permitAll()
.and()
.logout().logoutUrl("/logout")
.logoutSuccessUrl("/index?logout=true").permitAll()
.and()
.exceptionHandling().accessDeniedPage("/error");
}
}