How to exclude path from spring-security basic auth? - java

I'm using basic authentication with spring-security (security.basic.enabled=true), and want to exclude a certain path and all sub-paths.
The following does not work:
#Configuration
public class WildcardConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
//allow anonymous access to all sub paths
http.authorizeRequests().antMatchers("/my/**").permitAll();
}
}
#RestController
public class SubController {
#GetMapping("/my/sub/{id}")
public String test(#PathVariable id) {
return "got: " + id;
}
}
When I call localhost:8080/my/sub/123.
Result: 401 not authorized.
Why?

Use a different method:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(/my/**");
}

Related

How to use separate custom filters managed by Spring with multiple security configurations?

I've been trying to solve this problem for a couple of days now and given my limited experience with Spring I've run out of ideas.
I have a simple Hello World Spring Boot app with security enabled. I need different security configs on different versions (v1 and v2). Meaning that I need two different filters (extending WebSecurityConfigurerAdapter) to be used by each of the configs. Filters also have to be managed by Spring so that env variables can be injected using #Value.
Security config:
#EnableWebSecurity(debug = true)
public class WebSecurityConfig {
#Order(1)
#Configuration
public static class Config1 extends WebSecurityConfigurerAdapter {
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("******** Config1:config");
http.antMatcher("/api/v1/**")
.authorizeRequests()
.antMatchers("/api/v1/greet").permitAll()
.anyRequest().authenticated();
}
}
#Order(2)
#Configuration
public static class Config2 extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("******** Config2:config");
http.antMatcher("/api/v2/**")
.authorizeRequests()
.antMatchers("/api/v2/greet").permitAll()
.anyRequest().authenticated();
}
}
}
Filters:
#Component
public class TestFilter1 extends BasicAuthenticationFilter {
#Value("foo")
private String foo;
public TestFilter1(AuthenticationManager authManager) {
super(authManager);
System.out.println("************ TestFilter1:constructor");
}
#Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
System.out.println("************ TestFilter1:doFilterInternal foo: " + this.foo);
chain.doFilter(req, res);
}
}
#Component
public class TestFilter2 extends BasicAuthenticationFilter {
#Value("bar")
private String bar;
public TestFilter2(AuthenticationManager authManager) {
super(authManager);
System.out.println("************ TestFilter2:constructor");
}
#Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
System.out.println("************ TestFilter2:doFilterInternal bar: " + this.bar);
SecurityContextHolder.getContext().setAuthentication(null);
chain.doFilter(req, res);
}
}
Controller:
#RestController
public class GreetingController {
#RequestMapping("/api/v1/greet")
public String greet1() {
System.out.println("************ GREETING V1");
return "Hello Greeting 1";
}
#RequestMapping("/api/v2/greet")
public String greet2() {
System.out.println("************ GREETING V2");
return "Hello Greeting 2";
}
}
My problem is that even without any explicit assigment of the filter (e.g. .addFilterBefore()) they are both attached to both configs.
So hitting this:
http://localhost:8010/api/v2/greet
Prints this:
************ TestFilter1:doFilterInternal foo: foo
************ TestFilter2:doFilterInternal bar: bar
************ GREETING V1
While hitting other version:
http://localhost:8010/api/v2/greet
Prints this:
************ TestFilter1:doFilterInternal foo: foo
************ TestFilter2:doFilterInternal bar: bar
************ GREETING V2
As you can see in both cases both filters are called. In the initial setup i had them #Autowired as properties within Config1/2 and attached using .addFilterBefore() but noticed that both filters are called regardless. More mysteriously removing addFilterBefore() they are still called.
So the question probably would be - How to make to make those filters not be attached automatically whilst still having them as Spring components?
Thanks in advance!

Spring boot security using multiple WebSecurityConfigurerAdapter with different AuthenticationProviders

I am trying to achieve different authentications based on certain http requests, but it appears no matter what the requests are they use the wrong authentications. More specifically, for any manage endpoint, I want to use only authentication A and for any internal or api endpoints I want to use only authentication B.
For example, with the current code, if I send an invalid authentication/user for manage, it will first try to use Authenication A, then fails, and then tries to use authentication B (which is should not).
For manage:
#Configuration
#Order(1)
#EnableWebSecurity
public class SecurityConfigA extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/manage/**")
.authenticated().anyRequest().permitAll().and().httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(new AuthenticationProvider() {
#Override
public boolean supports(Class<?> arg0) {
return true;
}
#Override
public Authentication authenticate(Authentication auth) {
//do authentication A
}
});
}
}
For api and internal:
#Order(2)
#EnableWebSecurity
public class SecurityConfigB extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers(
"/internal/**",
"/api/**")
.authenticated().anyRequest().permitAll().and().httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(new AuthenticationProvider() {
#Override
public boolean supports(Class<?> arg0) {
return true;
}
#Override
public Authentication authenticate(Authentication auth) {
//do authentication B
}
});
}
}

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-boot setup basic auth on a single web app path?

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();
}
}

Categories

Resources