Spring Security configure httpsecurity - java

I'm taking my first try on the Spring Security and really got stuck on such a task: i have a default webpage, which should be defaulty non-authenticated, and i have a batch of controller calls, which i want to secure with a PreAuthorized annotation. The basic idea is, that i want to disable the default "redirect to login page", but still have the opportinity to operate the Spring Security's method security complex.
I'm using a java configuration, which looks like:
#Configuration
#EnableWebSecurity
public class SpringWebSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/res/**"); // #3
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests().anyRequest().permitAll();
}
}
I know(or seem to understand) that by this point all of my calls should be permitted(have been sitting over this for the past two days, and clearly running out of ideas).
The Controller's method, which i want to secure, is:
#PreAuthorize("hasRole('ADMIN')")
#RequestMapping(value="/admin", method = RequestMethod.GET)
public String getAdminPage(Model model){
return "admin";
}
I know that i can use antMatcher to add "/**/admin" and authorize the calls to the specific url, but the general idea is:
Disable the "go to login page" on the root(and other random controller mappings).
Do a manual ajax based authentication from a ajax drop down(or something).
When a random non-autherised user bumps in a page, which has a #PreAuthorized on a Controller, then, and only then, he should be redirected.
UPD: the basic question is to invoke the redirect to login page only on access denied scenario, allowing the anonymous role for basic site view's and calls.

Answering my own question(maybe not as clean as it should look).
You can config the Spring Security Http Security so it wont ask to login on every page:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests().anyRequest().permitAll();
To enable the method security(the PreAuthorize("hasRole('ADMIN')") and ect), you need to add an annotation:
#EnableGlobalMethodSecurity(prePostEnabled = true)
after which you need to add to the HttpSecurity object something to catch the exceptions of "Access Denied and ect" (found this on some other stackoverflow question thread):
http.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
if (authException != null) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().print("Unauthorizated....");
}
}
});
And now you can secure your controller and other components with #PreAutherized. Hope this will help someone.
But there still remains one thing - when the user is unauthorized and i try to reach some preAuthorized page, the above mentioned exception handler is invoked, returning the "Unauthorized..." message. But when the user is authorized and i try a page with a diffirent preAuthorized role, i get the default 403 Access Denied.

Related

Spring security default configuration always throws 401

I'm creating a new Spring REST application with some basic services and entities.
I added Spring Security and without overriding any class, i just added to application.properties a user and password.
So far so good, i opened Postman to try out a endpoint and it always return 401 to my requests.
I tried in postman set the authorization via "Basic Auth" (is what header WWW-Authenticate asks), tried "Digest auth" using the "Realm" value from the header. But none of it works.
Here is what i have in my application.properties
spring.security.user.name=root
spring.security.user.password=root
This is my request
https://imgur.com/URM3TGD
(Sorry i can't embbed the image because of my reputation)
And here is the endpoint
#PostMapping("saveUsuario")
public Usuario saveUsuario(Usuario usuario) {
return usuarioRepository.save(usuario);
}
(If possible) i don't want to override any Spring Security class, just "use as it".
Thank you!
So here is what i found.
Thanks to #jzheaux we discover that the problem was with the csrf configuration (Using POST request).
So i was forced to override the class WebSecurityConfigurerAdapter to disable it.
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
}
But then, the endpoints could be called without authentication!
So, this is the final code:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.cors();
http.authorizeRequests().anyRequest().fullyAuthenticated();
http.httpBasic();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
}
}
First disable the CSRF.
Then enable Cors.
I set that i want any request to be fully authenticated
The challenge type is HTTP basic
I disable the creation of cookies so it'll always ask for credentials.
So far so good, it's working!
Per https://docs.spring.io/spring-boot/docs/1.5.0.RELEASE/reference/htmlsingle/#boot-features-security
you should change your password with
security.user.password=root
instead of spring.security.user.password=root
similar security properties that are overridable are in the #ConfigurationProperties class: SecurityProperties.java
See https://github.com/spring-projects/spring-boot/blob/v1.5.0.RELEASE/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityProperties.java

How to set default #AuthenticationPrincipal on startup

For development purpose, I am trying to configure a development profile for which developers don't need to be authenticated in the application to call REST services.
But, some of these services need an #AuthenticationPrincipal to work.
So I would like to be able to define a mocked #AuthenticationPrincipal on startup to be used by default.
Does anyone have any kind of idea to do so?
Currently the application behavior expected for the user authentication is:
A REST endpoint should send a HTTP code 401 if the user isn't authenticated.
In this case, the Front-end should redirect the user to the back-end URL /login so that he can authenticate itself.
On success, the back-end should then redirect the user to the front-end.
It turn out that it wasn't a good solution, here a list of the different reasons I can think of:
The behavior of the back-end server would be different between the development and the production environments.
This would force the front-end to also have different behaviors between these two environments.
JUnits wouldn't be able to test all expected answers (example: HTTP code 401 if not authenticated) from endpoints.
So in place, I have created a security configuration (enabled only when not using the production profile) emulating the expected behavior from the front-end point of view.
Here the MockAuthenticationSecurityConfiguration class:
#Configuration
#Profile("!PRODUCTION")
public class MockAuthenticationSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final ApplicationProperties applicationProperties;
public MockAuthenticationSecurityConfiguration(final ApplicationProperties applicationProperties) {
this.applicationProperties = applicationProperties;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("swagger-ui.html").permitAll();
// All API REST endpoint can only be accessed by an authenticated user.
http.authorizeRequests().antMatchers("/api/**").authenticated()
// For these REST endpoint to answer HTTP code 401 in place of redirecting the user to /login.
.and().exceptionHandling().defaultAuthenticationEntryPointFor(new Http401UnauthorizedEntryPoint(), new AntPathRequestMatcher("/api/**"))
// On success, we want to redirect the user to a specific URL (the frontend).
.and().formLogin().permitAll().successHandler(new SimpleUrlAuthenticationSuccessHandler(applicationProperties.getRedirectUrl()))
.and().logout().permitAll()
;
http.csrf().disable();
http.cors();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// Add a mocked user to be used to authenticate.
auth.inMemoryAuthentication().withUser(User.withDefaultPasswordEncoder().username("jdoe").password("jdoe").roles("USER"));
}
}
Here the Http401UnauthorizedEntryPoint class:
public class Http401UnauthorizedEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
}
}
And then the ApplicationProperties class:
#Getter
#Setter
#Component
#ConfigurationProperties("application")
public class ApplicationProperties {
/**
* The URL to which we should redirect the user once he is logged in the application.
*/
private String redirectUrl;
}

Authentication is required to obtain an access token (anonymous not allowed)

I try to modify existing example - Tonr2 and Sparklr2.
Also I viewed this tutorial based on Spring Boot Spring Boot OAuth2. I try to build application like in Tonr2 example but without first login (on tonr2). I just need one Authentication on Sparklr2 side. I do this:
#Bean
public OAuth2ProtectedResourceDetails sparklr() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setId("sparklr/tonr");
details.setClientId("tonr");
details.setTokenName("oauth_token");
details.setClientSecret("secret");
details.setAccessTokenUri(accessTokenUri);
details.setUserAuthorizationUri(userAuthorizationUri);
details.setScope(Arrays.asList("openid"));
details.setGrantType("client_credentials");
details.setAuthenticationScheme(AuthenticationScheme.none);
details.setClientAuthenticationScheme(AuthenticationScheme.none);
return details;
}
But I have Authentication is required to obtain an access token (anonymous not allowed) . I checked this question. Of course, my user is anonymous - I want to login on Sparklr2. Also, I tried different combinations of settings of this bean, but nothing good. How to fix it? How to make it work as I want?
Almost two years late for the post.
The exception is thrown from AccessTokenProviderChain
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth instanceof AnonymousAuthenticationToken) {
if (!resource.isClientOnly()) {
throw new InsufficientAuthenticationException(
"Authentication is required to obtain an access token (anonymous not allowed)");
}
}
You either
Use ClientCredentialsResourceDetails in your OAuth2RestTemplate, or
Authenticate the user before using AuthorizationCodeResourceDetails to access external resources
In fact, in the tonr2 and sparklr2 example (I personally find the name very confusing), to access resources on sparklr2, a user has to first authenticate on tonr2. As seen in oauth2/tonr:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("marissa").password("wombat").roles("USER").and().withUser("sam")
.password("kangaroo").roles("USER");
}
If your user is anonymous, you might want to check for Single Sign On.
For whoever just want to quickly try out Oauth2 integration, add basic auth to your application:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated().and().httpBasic();
}
application.properties:
spring.security.user.password=password
spring.security.user.name=user
Don't forget to add spring-boot-starter-security to your project.
e.g. In gradle: compile 'org.springframework.boot:spring-boot-starter-security'
Or you can also disable AnonymousAuthenticationToken from creating by:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.anonymous().disable();
}
Old post...
The exception indeed is thrown form AccessTokenProviderChain but it happens when spring security filters invoking if incorrect order. Make sure that your OpenIdAuthenticationFilter is invoking after OAuth2ClientContextFilter.

spring security - change logout success url dynamically

I'm developing a java spring mvc project. This is a part of my securityConfig class:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
....
.logoutSuccessUrl("/loginForm")
...
}
When a user logs out, spring redirects him to the loginForm page. But, I want to change this url, dynamically. In fact, I want to redirect users to different pages based on some conditions. How can I do this?
See LogoutSuccessHandler:
Strategy that is called after a successful logout by the LogoutFilter, to handle redirection or forwarding to the appropriate destination.
For Java Configuration see LogoutConfigurer#logoutSuccessHandler.
Your modified source code:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.logout().logoutSuccessHandler(myLogoutSuccessHandler);
}

Using Spring Security with zero-legged Oauth 1

I am writing an LTI application using Spring boot. LTI applications are basically a plug-in for a learning management system (in this case Canvas) which work by sending an Oauth1 signed POST to my server. The result of this request is displayed to the user inside of an iframe. There is a pre-shared key and secret that the LMS uses to sign the request. If the signature on the POST checks out, I have an authenticated user. I have this part working, partially based on this question.
During the initial request (which comes to the URL "/launch") I can call SecurityContextHolder.getContext().getAuthentication() and use this without problems. My problem is when the user makes another request, say for a picture or by clicking on a link in my content, the SecurityContext object isn't following them. I'm pretty sure I'm not setting up the Spring security filter chain correctly so the SecurityContextPersistenceFilter isn't being hit on subsequent requests. At the end of the day, SecurityContextHolder.getContext().getAuthentication() returns null.
The OAuth signature verification happens in a WebSecurityConfigurerAdapter like so: (again, based on this)
#Configuration
public static class OAuthSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
//spring auto-wiring to set up the
//zeroLeggedOauthProviderProcessingFilter (see linked question)
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/launch")
.addFilterBefore(zeroLeggedOAuthProviderProcessingFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests().anyRequest().hasRole("OAUTH")
.and().csrf().disable();
}
}
So this works and creates an authenticated principal and everything. But due to that antMatcher, it only applies to the /launch path.
It seems like it should be simple to add another security configurer adapter that will ensure that all other paths in the application are protected by an authenticated session and in so doing would cause the SecurityContext associated with this user to become available but I have been unable to come up with the magic sauce. The documentation focuses more on standard login form based authentication setups. I'm also kind of new to Spring in general so I'm clearly missing something. I tried this but it causes all other requests to return a 403:
#Configuration
public static class SessionSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().hasRole("OAUTH")
.and().csrf().disable();
}
}

Categories

Resources