spring-boot setup basic auth on a single web app path? - java

I am trying to setup a single path (/basic) in my spring-boot spring MVC based application to be basic auth protected. I am just going to configure this using my own custom configuration parameters so the username and password are simply "admin" and "admin".
This currently works for the /basic path (I am prompted and can login correctly). The problem is that logout does not work (and I am not sure why) and also other paths (like /other shown) are being asked for basic auth credentials (before always being denied).
static class MyApplicationSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/open").permitAll();
http.authorizeRequests().antMatchers("/other").denyAll(); // Block it for now
http.authorizeRequests().antMatchers("/basic").authenticated().and().httpBasic().and().logout().logoutUrl("/basic/logout").invalidateHttpSession(true).logoutSuccessUrl("/");
}
}
I expected /other to always be denied but I don't get why basic auth is coming up for it. /open works as expected. I also don't understand why /basic/logout does not log me out (it also does not produce error messages). I do have a simple bit of code as a placeholder for the logout endpoint but if I do not have that then I get a 404. The "home" view is my web app root so I just want to send the user there after logout.
#RequestMapping("/logout")
public ModelAndView logout() {
// should be handled by spring security
return new ModelAndView("home");
}
UPDATE:
Here is the solution that seemed to work in the end (except the logout part, still not working):
#Configuration
#Order(1) // HIGHEST
public static class OAuthSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/oauth").authorizeRequests().anyRequest().denyAll();
}
}
#Configuration
public static class BasicAuthConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/basic").authorizeRequests().anyRequest().authenticated().and().httpBasic();
http.logout().permitAll().logoutUrl("/logout").logoutSuccessUrl("/").invalidateHttpSession(true);
//.and().logout().logoutUrl("/basic/logout").invalidateHttpSession(true).logoutSuccessUrl("/");
}
}

i'm not sure about the logout, but we had a similar problem with having some of our site under basic and some of it not. Our solution was to use a second nested configuration class only for the paths that needed http basic. We gave this config an #Order(1)..but i'm not sure if that was necessary or not.
Updated with code
#Configuration
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
private static final Logger LOG = LoggerFactory.getLogger(SecurityConfig.class);
#Autowired
public void registerAuthentication(AuthenticationManagerBuilder auth, Config appConfig) throws Exception {
auth.inMemoryAuthentication()
.withUser(appConfig.getString(APIConfig.CONFIG_KEY_MANAGEMENT_USER_NAME))
.password(appConfig.getString(APIConfig.CONFIG_KEY_MANAGEMENT_USER_PASS))
.roles(HyperAPIRoles.DEFAULT, HyperAPIRoles.ADMIN);
}
/**
* Following Multiple HttpSecurity approach:
* http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/#multiple-httpsecurity
*/
#Configuration
#Order(1)
public static class ManagerEndpointsSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/management/**").authorizeRequests().anyRequest().hasRole(HyperAPIRoles.ADMIN).and()
.httpBasic();
}
}
/**
* Following Multiple HttpSecurity approach:
* http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/#multiple-httpsecurity
*/
#Configuration
public static class ResourceEndpointsSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
//fyi: This adds it to the spring security proxy filter chain
.addFilterBefore(createBBAuthenticationFilter(), BasicAuthenticationFilter.class)
;
}
}
}
this seems to secure the actuator endpoints at /management with basic auth while the others work with a custom auth token header. We do not prompt for credentials (no challenge issued) though for anything..we'd have to register some other stuff to get that going (if we wanted it).
Hope this helps

only one path will be protected
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception
{
auth.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("user"))
.roles("USER");
}
#Configuration
#Order(1)
public static class ManagerEndpointsSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/add/**").authenticated()
.anyRequest().permitAll()
.and()
.httpBasic()
.and().csrf().disable();
}
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

Related

Spring Security - multiple security config requirement - adding basic auth to existing REST API Authentication

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
}
}
}

How can I filter specific paths with a filter and authenticate with LDAP on /login only?

I'm trying to authenticate with Basic Auth though LDAP but I only need to do so with /login route. All the other routes need to be authenticated through a custom JWT Filter that I implemented. Actually, what I've done just doesn't work.
Edit : just to be more clear, at the moment when I call on /auth/login, it goes through the LDAP filter and not the JWT Filter. But when I call any other route, it goes through JWT Filter and then still goes through the LDAP one and always makes my request fail. The behaviour is only correct when it's the /auth/login call.
Here's the configuration :
public class SecurityConfiguration {
#Configuration
#EnableWebSecurity
#Order(Ordered.HIGHEST_PRECEDENCE + 21)
public static class CompanySecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private LdapContextSource ldapContextSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication().contextSource(ldapContextSource).userDnPatterns("uid={0},ou=people");
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.httpBasic().and()
.requestMatchers()
.antMatchers("/auth/login")
.and().authorizeRequests().anyRequest().authenticated()
.and().csrf().disable();
}
}
#Configuration
#EnableWebSecurity
#Order(Ordered.HIGHEST_PRECEDENCE + 20)
public static class JWTSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.requestMatchers()
.antMatchers("/projects", "/projects/**", "/credential/**")
.and()
.addFilterAfter(new JWTFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests().anyRequest().authenticated()
.and().cors().disable();
}
}
}

Disable X-FrameOptions response header for a URL Spring Security JAVA config

I am trying to disable or set the XFrameOptions header to SAME_ORIGIN for a particular URL in my Spring Boot project with Spring Security. I am pasting the code below,
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
RequestMatcher matcher = new AntPathRequestMatcher("**/course/embed/**");
DelegatingRequestMatcherHeaderWriter headerWriter =
new DelegatingRequestMatcherHeaderWriter(matcher,new XFrameOptionsHeaderWriter());
http.headers()
.frameOptions().sameOrigin()
.addHeaderWriter(headerWriter);
}
}
I am using AntRequestMatcher but that does not work, it instead disabled the XFrameOptions header for all the responses. Is there a better way to do this? Please help.
You need to configure multiple HttpSecurity instances. The key is to extend the WebSecurityConfigurationAdapter multiple times. For example, the following is an example of having a different configuration for URL’s that match with **/course/embed/**. If matches X-Frame-Options will be SAMEORIGIN, otherwise DENY.
#EnableWebSecurity
public class WebMVCSecurity {
//Configure Authentication as normal, optional, showing just as a sample to indicate you can add other config like this
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
// Create an instance of WebSecurityConfigurerAdapter that contains #Order to specify which WebSecurityConfigurerAdapter should be considered first.
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
// The http.antMatcher states that this HttpSecurity will only be applicable to URLs that match with **/course/embed/**
http.antMatcher("**/course/embed/**").headers().frameOptions().sameOrigin();
}
}
// Create another instance of WebSecurityConfigurerAdapter.
// If the URL does not match with **/course/embed/** this configuration will be used.
// This configuration is considered after ApiWebSecurityConfigurationAdapter since it has an #Order value after 1 (no #Order defaults to last).
#Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
//bla bla bla ...
}
}
}
Another option is to:
Disable the default spring security which uses a XFrameOptionsHeaderWriter to add X-Frame-Options to responses
Configure a new HeaderWriter that only delegates to an XFrameOptionsHeaderWriter for the paths you actually want X-Frame-Options to be added to
Sample code:
public class AlignSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.headers()
.frameOptions().disable()
.addHeaderWriter(new CustomXFrameOptionsHeaderWriter());
}
private static class CustomXFrameOptionsHeaderWriter implements HeaderWriter {
private final XFrameOptionsHeaderWriter defaultHeaderWriter;
private static final Set<String> ALLOWED_TO_EMBED_IN_IFRAME = ImmutableSet.of("/some/path");
public CustomXFrameOptionsHeaderWriter()
{
this.defaultHeaderWriter = new XFrameOptionsHeaderWriter(XFrameOptionsMode.DENY);
}
#Override
public void writeHeaders(HttpServletRequest request, HttpServletResponse response)
{
if (!ALLOWED_TO_EMBED_IN_IFRAME.contains(request.getRequestURI()))
{
defaultHeaderWriter.writeHeaders(request, response);
}
}
}
}

Spring security - httpbasic not working: What am i doing wrong?

I'm new to spring security. Try to use it for project with a rest backend. For my backend certain urls need to be open, certain urls need to have httpbasic auth / https and certain urls need a token authentication.
I'm trying to set this up using a test with web mvc. Trying to test it by using controller methods:
#RequestMapping(value="/auth/signup", method=RequestMethod.POST)
#ResponseStatus(HttpStatus.OK)
public void test(){
System.err.println("Controller reached!");
}
#RequestMapping(value="/auth/login", method=RequestMethod.POST)
#ResponseStatus(HttpStatus.OK)
public void test2(){
System.err.println("Controller reached!");
}
My Spring Security Config locks like the following:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
#Configuration
#Order(1)
public static class FreeEndpointsConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/auth/signup").permitAll()
.and().csrf().disable();
}
}
#Configuration
#Order(2)
public static class HttpBasicAuthConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/auth/login").hasAnyRole("USER")
.and().httpBasic()
.and().csrf().disable();
}
}
}
My Test looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={RootContext.class, WebSecurityConfig.class})
#WebAppConfiguration
public class AccountSecurityTest {
#Autowired
private WebApplicationContext wac;
private MockMvc securityMockMvc;
#Before
public void SetupContext() {
securityMockMvc = MockMvcBuilders
.webAppContextSetup(wac)
.apply(springSecurity()).build();
}
#Test
public void testSigInFree() throws Exception {
MockHttpServletRequestBuilder post = post("/auth/signup");
securityMockMvc.perform(post).andExpect(status().isOk());
}
#Test
public void testLoginHttpBasic() throws Exception {
MockHttpServletRequestBuilder post = post("/auth/login");
securityMockMvc.perform(post).andExpect(status().isOk());
}
}
The testmethod "testLoginHttpBasic" is green. But I would expect a failure, because i'm trying to configure / enforce httpbasic authentication. Where is my mistake?
Change
http.authorizeRequests().antMatchers("/auth/signup").permitAll()
to
http.antMatcher("/auth/signup").authorizeRequests().anyRequest().permitAll()
and
http.antMatcher("/auth/login").authorizeRequests().anyRequest().hasAnyRole("USER")
to
http.authorizeRequests().antMatchers("/auth/login").hasAnyRole("USER").
Your second test will fail.
Why do you need this change?
http.authorizeRequests()... creates a SecurityFilterChain that matches every URL. As soon as one SecurityFilterChain matches the request all subsequent SecurityFilterChains will never be evaluated. Hence, your FreeEndpointsConfig consumed every request.
With http.antMatcher("...") in place you restrict every SecurityFilterChain to a particular URL (pattern). Now FreeEndpointsConfig matches only /auth/signup and HttpBasicAuthConfig /auth/login.
Small improvement
You can make several URLs like paths to static resources (js, html or css) public available with WebSecurity::configure. Override WebSecurity::configure in your WebSecurityConfig
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity
.ignoring()
.antMatchers("/auth/signup");
}
and FreeEndpointsConfig isn't required anymore.

Spring Security - Authentication not firing up

I created a Spring Boot - Security project based on a example app, however the Rest Controller works fine, means, I can access the rest resource but the security does not seem to fire up at all, so when I added a breakpoint as stated below it does not break there. Not sure why.
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// #formatter:off
auth.inMemoryAuthentication() // breakpoint here
.withUser("roy")
.password("spring")
.roles("ADMIN");
// #formatter:on
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Here is the complete project hosted and editable with Codio: http://bit.ly/1uFI0t5
You have to tell the framework which are the urls that it have to secure.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").access("hasRole('ROLE_USER')")
.and().formLogin();
}
}

Categories

Resources