I am trying to set up multiple WebsecurityConfigurerAdapter for my project where the spring boot actuator APIs are secured using basic auth and all other endpoints are authenticated using JWtAuthentication. I am just not able to make it work together, only the config with the lower order works. I am using Spring Boot 2.1.5.RELEASE
Security Config One with JWT Authenticator
#Order(1)
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] AUTH_WHITELIST = {
"/docs/**",
"/csrf/**",
"/webjars/**",
"/**swagger**/**",
"/swagger-resources",
"/swagger-resources/**",
"/v2/api-docs"
};
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.antMatchers("/abc/**", "/abc/pdf/**").hasAuthority("ABC")
.antMatchers("/ddd/**").hasAuthority("DDD")
.and()
.csrf().disable()
.oauth2ResourceServer().jwt().jwtAuthenticationConverter(new GrantedAuthoritiesExtractor());
}
}
The basic Auth config with username/password
#Order(2)
#Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
/* #Bean
public UserDetailsService userDetailsService(final PasswordEncoder encoder) {
final InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(
User
.withUsername("user1")
.password(encoder.encode("password"))
.roles("ADMIN")
.build()
);
return manager;
}
#Bean PasswordEncoder encoder(){
return new BCryptPasswordEncoder();
}*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/actuator/**").hasRole("ADMIN")
.and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user1").password("password").authorities("ADMIN");
}
}
I have been trying to make it work for many days but cannot make both of them work together. If i swap the order, only basic auth works and not the JWT Auth Manager.
I have gone through a lot of SOF Questions, like
[https://stackoverflow.com/questions/40743780/spring-boot-security-multiple-websecurityconfigureradapter][1]
[https://stackoverflow.com/questions/52606720/issue-with-having-multiple-websecurityconfigureradapter-in-spring-boot][1]
[https://github.com/spring-projects/spring-security/issues/5593][1]
[https://www.baeldung.com/spring-security-multiple-entry-points][1]
Nothing seems to be working, is this a known issue in Spring?
To use multiple WebsecurityConfigurerAdapter, you need restrict them to specific URL patterns using RequestMatcher.
In your case you can set a higher priority for ActuatorSecurityConfig and limit it only to actuator endpoints:
#Order(-1)
#Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/actuator/**")
.and()
.authorizeRequests().anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
Related
I am trying to set up multiple WebsecurityConfigurerAdapter for my project where the spring boot actuator APIs are secured using basic auth and all other endpoints are authenticated using JWtAuthentication. I am just not able to make it work together, only the config with the lower order works. I am using Spring Boot 2.1.5.RELEASE
Security Config One with JWT Authenticator
#Order(1)
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] AUTH_WHITELIST = {
"/docs/**",
"/csrf/**",
"/webjars/**",
"/**swagger**/**",
"/swagger-resources",
"/swagger-resources/**",
"/v2/api-docs"
};
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.antMatchers("/abc/**", "/abc/pdf/**").hasAuthority("ABC")
.antMatchers("/ddd/**").hasAuthority("DDD")
.and()
.csrf().disable()
.oauth2ResourceServer().jwt().jwtAuthenticationConverter(new GrantedAuthoritiesExtractor());
}
}
The basic Auth config with username/password
#Order(2)
#Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
/* #Bean
public UserDetailsService userDetailsService(final PasswordEncoder encoder) {
final InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(
User
.withUsername("user1")
.password(encoder.encode("password"))
.roles("ADMIN")
.build()
);
return manager;
}
#Bean PasswordEncoder encoder(){
return new BCryptPasswordEncoder();
}*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/actuator/**").hasRole("ADMIN")
.and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user1").password("password").authorities("ADMIN");
}
}
I have been trying to make it work for many days but cannot make both of them work together. If i swap the order, only basic auth works and not the JWT Auth Manager.
I have gone through a lot of SOF Questions, like
[https://stackoverflow.com/questions/40743780/spring-boot-security-multiple-websecurityconfigureradapter][1]
[https://stackoverflow.com/questions/52606720/issue-with-having-multiple-websecurityconfigureradapter-in-spring-boot][1]
[https://github.com/spring-projects/spring-security/issues/5593][1]
[https://www.baeldung.com/spring-security-multiple-entry-points][1]
Nothing seems to be working, is this a known issue in Spring?
To use multiple WebsecurityConfigurerAdapter, you need restrict them to specific URL patterns using RequestMatcher.
In your case you can set a higher priority for ActuatorSecurityConfig and limit it only to actuator endpoints:
#Order(-1)
#Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/actuator/**")
.and()
.authorizeRequests().anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
I have spring boot app with thymeleaf. I am using spring security formLogin method for security and now I need to add JWT for only some APIs.
#EnableWebSecurity
public class SecurityConfigurations {
#Autowired
UserDetailsServiceImpl userDetails;
#Bean
DaoAuthenticationProvider provider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(encoder());
provider.setUserDetailsService(userDetails);
return provider;
}
#Bean
PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
#Configuration
#Order(1)
public class JWTSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
DaoAuthenticationProvider provider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(provider);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.authorizeRequests().antMatchers("/api/user/authenticate").permitAll()
.antMatchers("/api/user/**").hasRole("USER")
.and().
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
#Configuration
public static class FormLoginConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
DaoAuthenticationProvider provider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(provider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/admins**").hasAnyRole("SADMIN").antMatchers("/admin/**")
.hasAnyRole("ADMIN", "SADMIN", "WADMIN").antMatchers("/rest/**")
.hasAnyRole("ADMIN", "SADMIN", "WADMIN", "USER").antMatchers("/user/**").hasAnyRole("USER")
.anyRequest().permitAll().and().formLogin().loginPage("/sign-in-up")
.loginProcessingUrl("/signInProcess").usernameParameter("phone").and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/")
.invalidateHttpSession(false).and().csrf().disable().cors();
}
}
}
by doing this JWT is working fine as just I need but the formlogin has stopped and calling "/signInProcess" now give 404:
NOTE: if I change the order and make formLogin #order(1) it works again but of course will not work.
Also I tried to combine them both like this now it is both works fine but the problem with exception handling if the JWT authentication error will return formlogin thymeleaf error page :
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/admins**").hasAnyRole("SADMIN").antMatchers("/admin/**")
.hasAnyRole("ADMIN", "SADMIN", "WADMIN").antMatchers("/rest/**")
.hasAnyRole("ADMIN", "SADMIN", "WADMIN", "USER").antMatchers("/user/**").hasAnyRole("USER")
.antMatchers("/api/user/authenticate").permitAll()
.antMatchers("/api/user/**").hasRole("USER")
.anyRequest().permitAll().and().formLogin().loginPage("/sign-in-up")
.loginProcessingUrl("/signInProcess").usernameParameter("phone").and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/")
.invalidateHttpSession(false).and().csrf().disable().cors();
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
any suggestions to make this work. thank you.
Your WebSecurityConfigurerAdapters will process the incoming requests in order.
Since JWTSecurityConfig is annotated with #Order(1) it will process the requests first.
You have not specified a antMatcher for this Adapter, so it will match all requests.
This means that a request will never reach FormLoginConfigurationAdapter, since JWTSecurityConfig matches them all.
If you want JWTSecurityConfig to only apply to certain requests, you can specify an antMatcher in your security configuration.
Below is an example:
#EnableWebSecurity
public class SecurityConfigurations {
#Configuration
#Order(1)
public class JWTSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers(matchers -> matchers
.antMatchers("/api/**") // apply JWTSecurityConfig to requests matching "/api/**"
)
.authorizeRequests(authz -> authz
.anyRequest().authenticated()
)
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
#Configuration
public class FormLoginConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authz -> authz
.anyRequest().authenticated()
)
.formLogin();
}
}
}
For more details on multiple WebSecurityConfigurerAdapter, you can see the multiple HttpSecurity section in the Spring Security reference docs.
For more details on the difference between authorizeRequests() and requestMatchers(), you can see this Stack Overflow question.
I have a REST API with the following security config -
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Value("${auth0.audience}")
private String audience;
#Value("${auth0.issuer}")
private String issuer;
#Override
protected void configure(HttpSecurity http) {
try {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/purch").authenticated()
.antMatchers("/purch2").authenticated();
JwtWebSecurityConfigurer
.forRS256(audience, issuer)
.configure(http);
} catch (Exception ex) {
throw new AuthenticationException(ex.getMessage());
}
}
}
I had added Swagger docs for this REST API and I am trying to protect the swagger docs using HTTP Basic Auth using this example
Hence, I updated the above WebSecurityConfig with #Order(1) and added a new WebSecurityConfig with Order(2) as shown below -
#Configuration
#Order(2)
public class SwaggerSecurity extends WebSecurityConfigurerAdapter {
private static final String[] AUTH_LIST = { //
"**/v2/api-docs", //
"**/configuration/ui", //
"**/swagger-resources", //
"**/configuration/security", //
"**/swagger-ui.html", //
"**/webjars/**" //
};
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers(AUTH_LIST).authenticated().and().httpBasic();
}
//#Override
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("password")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder().encode("admin")).roles("USER", "ADMIN");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
This does not seem to have any effect and is NOT prompting for the basic auth credentials.
I tried several combinations of answers from here, here and here... But I am unable to get this working!
I was able to get the standalone Order(2) spring web security config working as expected, just not in combination with Order(1) security config.
As you can see from my question, I am not an expert with Spring Security and tried debugging this as much as I can! its time I sought for help after losing couple of hours on this. Any help is appreciated. Thank you.
Update based on comments:
I already tried combining the Web Security Config classes similar to what is shown here or here. The outcome is that my original REST API which was protected with "Authorization Header" bearer authentication is now overriden with Basic Auth.
May be, my question is - how do I make sure that one Web security config does not override another?
#Configuration
#Order(2)
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Value("${auth0.audience}")
private String audience;
#Value("${auth0.issuer}")
private String issuer;
#Override
protected void configure(HttpSecurity http) {
try {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/purch").authenticated()
.antMatchers("/purch2").authenticated();
JwtWebSecurityConfigurer
.forRS256(audience, issuer)
.configure(http);
} catch (Exception ex) {
throw new AuthenticationException(ex.getMessage());
}
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
private static final String[] AUTH_LIST = { //
"/v2/api-docs", //
"/configuration/ui", //
"/swagger-resources", //
"/configuration/security", //
"/swagger-ui.html", //
"/webjars/**" //
};
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/purch/**").permitAll().and()
.authorizeRequests()
.antMatchers(AUTH_LIST)
.authenticated()
.and()
.httpBasic();
}
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("password")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder().encode("admin")).roles("USER", "ADMIN");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
}
You seem to have mixed up contents gleaned from different sources. Please try a configuration like below.
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// Firs this configuration will apply since the order is 1
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
// configure auth modes and path matchers
}
}
// Since there is no #Order annotation, this will be checked at last
#Configuration
public static class MvcWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
// configure auth modes and path matchers
}
}
}
This is my Spring Boot 1.5.1 Actuator application.properties:
#Spring Boot Actuator
management.contextPath: /actuator
management.security.roles=R_0
This is my WebSecurityConfig:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Value("${logout.success.url}")
private String logoutSuccessUrl;
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);
http
.csrf().ignoringAntMatchers("/v1.0/**", "/logout")
.and()
.authorizeRequests()
.antMatchers("/oauth/authorize").authenticated()
//Anyone can access the urls
.antMatchers("/signin/**").permitAll()
.antMatchers("/v1.0/**").permitAll()
.antMatchers("/auth/**").permitAll()
.antMatchers("/actuator/health").permitAll()
.antMatchers("/actuator/**").hasAuthority("R_0")
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.failureUrl("/login?error=true")
.usernameParameter("username")
.passwordParameter("password")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl(logoutSuccessUrl)
.permitAll();
// #formatter:on
}
/**
* Configures the authentication manager bean which processes authentication requests.
*/
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Right now I'm successfully able to login in my application with a right user that has R_0 authorities but when I trying to access for example
http://localhost:8080/api/actuator/beans
I receive a following error:
There was an unexpected error (type=Forbidden, status=403).
Access is denied. User must have one of the these roles: R_0
How to correctly configure Spring Boot Actuator in order to be aware about the correct Authentication ?
Right now in order to get it workin I have to do the following trick:
management.security.enabled=false
.antMatchers("/actuator/health").permitAll()
.antMatchers("/actuator/**").hasAuthority("R_0")
Is any chance to configure Actuator in a right way ?
UPDATED
I'm using UserDetailsService.UserDetails.Authorities
public Collection<? extends GrantedAuthority> getAuthorities() {
String[] authorities = permissions.stream().map(p -> {
return p.getName();
}).toArray(String[]::new);
return AuthorityUtils.createAuthorityList(authorities);
}
You have to use prefix ROLE_ for your management.security.roles for example management.security.roles=ROLE_SOMENAME in order to solve this issue
To have authorization to spring boot actuator endpoints you need to have ACTUATOR role.
Refer this example Accessing Restricted Actuator Endpoints with Spring Security
I'm coming at this from a Reactive Spring Boot 2.x app and had this problem and solved it by updating the WebSecurityConfig.securityWebFilterChain as well as SecurityContextRepository.load to include /actuator/** as follows:
public class WebSecurityConfig {
private AuthenticationManager authenticationManager;
private SecurityContextRepository securityContextRepository;
#Autowired
public WebSecurityConfig(AuthenticationManager authenticationManager, SecurityContextRepository securityContextRepository) {
this.authenticationManager = authenticationManager;
this.securityContextRepository = securityContextRepository;
}
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.exceptionHandling()
.authenticationEntryPoint((swe, e) -> Mono.fromRunnable(() -> {
swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
})).accessDeniedHandler((swe, e) -> Mono.fromRunnable(() -> {
swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
})).and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(authenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.pathMatchers("/actuator/**").permitAll()
.anyExchange().authenticated()
.and().build();
}
as well as updating
#Slf4j
#Component
public class SecurityContextRepository implements ServerSecurityContextRepository {
private AuthenticationManager authenticationManager;
public SecurityContextRepository(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Mono<Void> save(ServerWebExchange swe, SecurityContext sc) {
return Mono.error(new UnsupportedOperationException("Not supported"));
}
#Override
public Mono<SecurityContext> load(ServerWebExchange swe) {
ServerHttpRequest request = swe.getRequest();
if (request.getPath().value().startsWith("/actuator") ) {
return Mono.empty();
}
// other authentication logic here
}
1
1.You can add the following properties
endpoints.health.enabled=true
endpoints.loggers.enabled=true
endpoints.metrics.enabled=true
endpoints.env.enabled=false
endpoints.configprops.enabled=false
endpoints.autoconfig.enabled=false
endpoints.info.enabled=false
management.context-path=/management
2)
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements ApplicationContextAware {
private static final String ACTUATOR = "ACTUATOR";
private static final String ROLE1 = "USER";
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().antMatchers("/controllerpath/**")
.hasAnyRole(ROLE1).antMatchers("/loggers/**","/metrics/**","/health/**").hasAnyRole(ACTUATOR).and()
.csrf().disable().headers().frameOptions().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("username-of-app").password("encryptedpassword")
.roles(ROLE1).and().withUser("username-of-management-for-path")
.password("encrypted password").roles(ACTUATOR);
}
}
You can add the following properties
endpoints.health.enabled=true
endpoints.loggers.enabled=true
endpoints.metrics.enabled=true
endpoints.env.enabled=false
endpoints.configprops.enabled=false
endpoints.autoconfig.enabled=false
endpoints.info.enabled=false
management.context-path=/management
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements ApplicationContextAware {
private static final String ACTUATOR = "ACTUATOR";
private static final String ROLE1 = "USER";
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().antMatchers("/controllerpath/**")
.hasAnyRole(ROLE1).antMatchers("/loggers/**","/metrics/**","/health/**").hasAnyRole(ACTUATOR).and()
.csrf().disable().headers().frameOptions().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("username-of-app").password("encryptedpassword")
.roles(ROLE1).and().withUser("username-of-management-for-path")
.password("encrypted password").roles(ACTUATOR);
}
}
According to this link:
http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-monitoring.html
By default all sensitive HTTP endpoints are secured such that only
users that have an ACTUATOR role may access them. Security is enforced
using the standard HttpServletRequest.isUserInRole method.
Use the management.security.roles property if you want something
different to ACTUATOR.
So I think all you have to do is set the following property in application.properties.
management.security.roles
Ex:
management.security.roles=R_0
I'm trying to define two different security configurations for different url patterns, one of them using form login and another one using basic authentication for an api.
The solution I'm looking for is similar to the one explained here http://meera-subbarao.blogspot.co.uk/2010/11/spring-security-combining-basic-and.html but I would like to do it using java config.
Thanks in advance.
This is the configuration I currently have:
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
#Override
public void configure(WebSecurity web) throws Exception {
// Ignore any request that starts with "/resources/".
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls().antMatchers("/", "/index", "/user/**", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and().formLogin()
.loginUrl("/login")
.failureUrl("/login-error")
.loginProcessingUrl("/security_check")
.usernameParameter("j_username").passwordParameter("j_password")
.permitAll();
http.logout().logoutUrl("/logout");
http.rememberMe().rememberMeServices(rememberMeServices()).key("password");
}
#Bean
public RememberMeServices rememberMeServices() {
TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices("password", userService);
rememberMeServices.setCookieName("cookieName");
rememberMeServices.setParameter("rememberMe");
return rememberMeServices;
}
}
The solution I found was to create another class extending WebSecurityConfigurerAdapter inside the first one, like is described https://github.com/spring-projects/spring-security-javaconfig/blob/master/samples-web.md#sample-multi-http-web-configuration
My solution is as follows:
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
#Override
public void configure(WebSecurity web) throws Exception {
// Ignore any request that starts with "/resources/".
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls().antMatchers("/", "/index", "/user/**", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and().formLogin()
.loginUrl("/login")
.failureUrl("/login-error")
.loginProcessingUrl("/security_check")
.usernameParameter("j_username").passwordParameter("j_password")
.permitAll();
http.logout().logoutUrl("/logout");
http.rememberMe().rememberMeServices(rememberMeServices()).key("password");
}
#Bean
public RememberMeServices rememberMeServices() {
TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices("password", userService);
rememberMeServices.setCookieName("cookieName");
rememberMeServices.setParameter("rememberMe");
return rememberMeServices;
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("api").password("pass").roles("API");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls()
.antMatchers("/api/**").hasRole("API")
.and()
.httpBasic();
}
}
}
I would say by simply doing it. Specify a second line with authorizeUrls() but for your URLs that are needed with basic authentication. Instead of formLogin() use httpBasic()
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls().antMatchers("/", "/index", "/user/**", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and().formLogin()
.loginUrl("/login")
.failureUrl("/login-error")
.loginProcessingUrl("/security_check")
.usernameParameter("j_username").passwordParameter("j_password")
.permitAll();
http.authorizeUrls().antMatchers("/api/*").hasRole("YOUR_ROLE_HERE").and().httpBasic();
http.logout().logoutUrl("/logout");
http.rememberMe().rememberMeServices(rememberMeServices()).key("password");
}
Something like that should work.
Links: HttpSecurity, HttpBasicConfgurer.
You can solve it by adding .antMatcher("/api/**") just after http in your first config to manage only /api urls. You must have it on the first adapter:
http
.antMatcher("/api/*")
.authorizeRequests()
.antMatchers("^/api/.+$").hasRole("ADMIN")
....
Obviously As Spring updates these tend to fade in their applicability. Running spring cloud starter security 1.4.0.RELEASE this is my solution. My use case was a bit different as I'm trying to secure the refresh endpoint using basic auth for cloud configuration and using a gateway with spring session to pass the authentication in all other instances.
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal1(AuthenticationManagerBuilder auth) throws Exception {
//try in memory auth with no users to support the case that this will allow for users that are logged in to go anywhere
auth.inMemoryAuthentication();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/user").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable();
}
#Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder(11);
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationProvider customAuthenticationProvider;
#Autowired
protected void configureGlobal2(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(customAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/refresh").hasRole("SUPER")
.and()
.csrf().disable();
}
}
}
see the spring security docs for further clarification: Spring Security
The basic idea is that the #Order annotation will dictate what order the auth schemes are run. No #Order means that it is last. If the authorizeRequests() section cannot match on the incoming URL then that configuration will pass and the next one will attempt authentication. This will proceed until authentication succeeds or fails.
The other answers in here are relatively old and for example, I can't find authorizeUrls in Spring 4.
In SpringBoot 1.4.0 / Spring 4, I've implemented the basic/form login like this:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
protected void configure (HttpSecurity aHttp) throws Exception
{
aHttp.authorizeRequests ().antMatchers (("/api/**")).fullyAuthenticated ().and ().httpBasic ();
aHttp.formLogin ()
.loginPage ("/login").permitAll ()
.and ().logout ().permitAll ();
}
}
There may be more ellegant ways of writing this - I'm still working on understanding how this builder works in terms of sequence and so forth. But this worked.