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

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.

Related

Spring Oauth2 configure authorization endpoints

I have Spring web app with REST WS built using spring-security-oauth2:2.3.3 and spring-boot-starter-security:2.0.2. But I am not able to set which endpoints are protected.
ResourceServerConfig
#EnableResourceServer
#Configuration
public class ResourceServerConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/customers", "/v2/**").permitAll()
.antMatchers("/api/**").authenticated();
}
#Bean
public AuthenticationManager authenticationManager(AuthenticationProvider authenticationProvider) {
return new CustomAuthenticationManager(authenticationProvider);
}
#Bean
public AuthenticationProvider authenticationProvider(UserRepository userRepository, PasswordEncoder passwordEncoder) {
return new DBAuthenticationProvider(userRepository, passwordEncoder);
}
}
AuthorizationServerConfig
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("ClientId")
.secret("secret")
.authorizedGrantTypes("password")
.scopes("read", "write");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(authenticationManager);
}
}
When I obtain access token first, everything works perfectly, but I expect endpoints "/", "/customers", "/v2/**" to be permited for all without any authorization. But when I call 'curl http://{{host}}/' or http://{{host}}/customers I still get 401:
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
adding "--header "Authorization: Basic {{base64 client id:password}}" doesnt help either. How is that possible?
EDIT
according to spring security permitAll still considering token passed in Authorization header and returns 401 if token is invalid solved by adding #Order(1) to ResourceServerConfig to override default OAuth2. But that should kick in only when "Authorization" header is added, which is not my case. So how is this possible?
Add this to your project.
#Configuration
public class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/user/**")
.authorizeRequests().anyRequest().authenticated();
}
}
If you want to make any request to /user/** you need to pass your access token in the header as Authorization: Bearer {access_token}. All other endpoints will not ask for a token.

Unit test BasicAuth using Springboot

I've implemented BasicAuth in my SpringBoot application to authenticate few URLs. Somewhat like:
Configuration class
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${auth.authenticated}")
private String[] allAuthenticated;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(allAuthenticated).authenticated()
.and()
.httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) { 1
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
// .... Rest of the code
}
application.yml
auth.authenticated: /onlineshop/v1/ecart,/onlineshop/v1/wishlist
It is working fine, but I want to unit test this.
I was thinking of a simple test case where I can direclty make an HTTP request to either /onlineshop/v1/ecart OR /onlineshop/v1/wishlist and somehow check if they are authenticated or not.
I came across this MockMVC section, and coded below class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=WebSecurityConfig.class)
public class BasicAuthTest {
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity()) 1
.build();
}
#Test
public void shouldBeAuthenticated() throws Exception {
mvc.perform(get("/onlineshop/v1/ecart").with(httpBasic("user","password"))).andExpect(status().isOk());
}
}
But every time it gives me 404 error. So I'm not sure if I've configured it properly or what?
Also if there is some other better way to test BasicAuth, kindly suggest.
Thank You

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

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