I am building REST API with Spring Boot and I use Spring Security. I started here but found some other tutorials and blog posts with this issue and managed to get it work after implementing custom stuff. This and this SO posts answer some of my questions, but I have one more:
Is there any extension that implements some of the things like REST AuthenticationEntryPoint that returns 401 instead of redirect, or JWT generating and verifying or I should just implement same things for every REST service?
Thank you for your answers.
I also use Springboot but for the security I rely on Apache Shiro project which fundamentally, depending how you store the users accounts (mines are in a MongoDb instance),
takes care of the login - currentUser.login(token);
If fails throws an exception so you can handle the response
If succeed inject the authentication cookie in the response
For any other request, decode the cookie and inject the user with the proper authorizations
In few words Shiro does not redirect the HTTPRequest because it just care for the security leaving further decision, redirect in your case, to your controller logic.
You can add it to your project with a simple Maven dependence.
#brownies.....
try this one....
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
#Component
public class RESTAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
#Autowired
private RESTAuthenticationEntryPoint restAuthenticationEntryPoint;
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.cors().and().exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint).and().authorizeRequests()......
add above RESTAuthenticationEntryPoint and config in your security configuration class then it will return 401 if auth fails.
Related
I need to send an email every time a user logs into an account, but since SpringBoot is used I don't have a #PostMapping method for /login. What should I do?
From what you wrote, I assume that you want to send email only when user performs a successful login. You could write your implementation of AuthenticationSuccessHandler to send an email when user logs in succesfully.
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
#Component
public class MyNotifyingAuthSuccessHandler implements AuthenticationSuccessHandler {
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException {
//send email (do it asynchronously if you can)
//redirect to your homepage (page where user should land after login)
}
}
Sending an email sometimes takes a lot of time, so consider doing that async not to block the login operation for too long, if your business requirements allow you to do that.
I developed a Microservice Architecture with Spring. Therefor I have implemented one auth service and one userinterface service.
The Session is shared with redis. So The worklow is that you login on the auth-login page and after success you get redirected to the ui resource.
So far so good. I also implemented authorization by user role the secure my resources on the controller level.
What I want:
Even when login was successful it should not be possible change the url and enter a restricted area. So when a client tries to do this an Access-denied Exception gets thrown atm.
In that case I want to invalidate the session and redirect to the login page of the auth service.
What I did:
I followed this tutorial by baeldung to implement a custom accessdeniedhandler.
Code:
CustomAccessDeniedHandler.java
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
#Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.sendRedirect("http://localhost:8000/login");
}
}
SpringSecurity.java
#Configuration
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
}
#Bean
public AccessDeniedHandler accessDeniedHandler(){
return new CustomAccessDeniedHandler();
}
}
What happens:
I've set a breakpoint in my handler but it is not thrown and the general access-denied message gets thrown. Is there something I forgot to register in the implementation?
Thank you guys
Heee there,
right now I am totally stuck. I spend hours asking google but I haven't found any solution or approach yet. Maybe you guys can help me.
Some background information:
I am developing a microservice into an existing microservice infrastructure. I want to use spring boot and connect the service to our existing authentication service. There are plenty of other jax rs microservices which are already connected to it. I started with an authentication and an authorization filter. The authentication filter works perfectly.
The problem:
I want to use my own "Secured" annotation like in the other services.
So there are some annotated resource methods in controllers like this example one:
#Secured({Role.ADMIN,...})
#RequestMapping(value = "/interfaces", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ExportInvoiceInterfaceResponseRepListWrapper> getAll() {
...
}
so when the following filter gets triggered I want to read the roles of the annotated controller method. In jax rs I just used the Class ResourceInfo to do so. As you may know I can't use this class in a default spring boot setup. Is there any way to get the class "the spring boot way"?
public class AuthorizationFilter extends GenericFilterBean {
#Context
private ResourceInfo resourceInfo;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// Get the resource class which matches with the requested URL
// Extract the roles declared by it
Class<?> resourceClass = resourceInfo.getResourceClass();
List<Role> classRoles = extractRoles(resourceClass);
...
}
Any help would be awesome. Thank you in advanced.
Cheers
Frank
I am trying to get oauth2 to work with spring-boot and protect my rest method calls, sans much success.
I have tried using spring-security-oauth2-javaconfig:1.0.0.CI-SNAPSHOT with rg.springframework.boot:spring-boot-starter-security:1.0.0.RC1.
*gradle:
compile("org.springframework.boot:spring-boot-starter-security:1.0.0.RC1")
compile ('org.springframework.security.oauth:spring-security-oauth2-javaconfig:1.0.0.CI-SNAPSHOT'){
exclude module: 'spring-security-config'
exclude module: 'spring-security-core'
exclude module: 'spring-security-web'
}
For now I am just trying to get the authentication and resource server working. I have copied and tried to modify the existing sparklr2 sample from the spring-security-oauth2-javaconfig sample.
The last error I get is :"error":"invalid_client","error_description":"Bad client credentials
when I run curl -v --data "grant_type=password&username=marissa&password=koala&client_id=tonr&secret=secret" -X POST localhost:8100/oauth/token.
I understand oauth2 from a beginner's perspective and the paucity of resources with regard to oauth2 with spring-boot and rest make it hard. Any suggestions?
If someone could provide a cookbook like approach to configure oauth2 authentication and authorization to protect a rest api call along with the relevant curl commands, that would be awesome.,
Java config support for oauth2 is work in progress, but you might have more success with my fork. If I were you I'd stick to XML for the oauth2 bits for now. Here's a bootified sparklr2 with minimal XML. I haven't checked that it works recently but it shouldn't be in bad shape if you update the boot dependencies to 1.0.0.RC2.
Update: the #Configuration stuff has moved to the main OAuth2 repo, so the fork and its parent are basically redundant now (and will probably be removed soon).
Update: the bootified sample is now also using #Configuration.
Yes. This is what I have done to get it to work that way. I believe it is the right solution (other than using client_credentials for grant_type, but I am not an expert:-) If there is a better solution that would be awesome. Thank you so much for taking the time to help me out.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.config.annotation.authentication.configurers.InMemoryClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.OAuth2ServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.OAuth2ServerConfigurer;
import org.springframework.security.oauth2.provider.token.InMemoryTokenStore;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends OAuth2ServerConfigurerAdapter {
private final String applicationName = "restservice";
#Value("${client_id}")
private String client_id;
#Value("${client_secret}")
private String client_secret;
#Value("${grant_type}")
private String grant_type;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.and()
.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.apply(new OAuth2ServerConfigurer())
.tokenStore(new InMemoryTokenStore())
.resourceId(applicationName);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(new InMemoryUserDetailsManager(getUserDetails()))
.and()
.apply(new InMemoryClientDetailsServiceConfigurer())
.withClient(client_id)
.resourceIds(applicationName)
.scopes("read", "write")
.authorities("USER")
.authorizedGrantTypes(grant_type)
.secret(client_secret);
}
private static final Collection<UserDetails> getUserDetails() {
List<UserDetails> userDetails = new ArrayList<UserDetails>();
userDetails.add(new User("user", "password", AuthorityUtils.createAuthorityList(
"USER", "read", "write")));
return userDetails;
}
}
I'm trying to test a resource with Resteasy using an embedded Netty instance as described in the Resteasy Docs.
Injecting path parameters and query parameters works like a charm but then I tried to test a resource that injects HttpServletRequest and HttpServletResponse from the context like this:
#GET
#Path("/")
public void example(#Context HttpServletResponse response,
#Context HttpServletRequest request) { ... }
Resteasy cannot find HttpServletRequestin the context and throws the following exception:
5105 [r #1] DEB o.j.resteasy.core.SynchronousDispatcher - PathInfo: /auth
5201 [r #1] ERR c.s.f.v.s.r.e.ApplicationExceptionMapper - Unhandled application exception: Unable to find contextual data of type: javax.servlet.http.HttpServletRequest
org.jboss.resteasy.spi.LoggableFailure: Unable to find contextual data of type: javax.servlet.http.HttpServletRequest
I tried putting mock versions of request and response in the context as suggested in RESTEasy Mock vs. Exception Mapper vs. Context but it does not work either as the contextual data is a ThreadLocal and Netty spawns a new thread for each request.
Any ideas on how to solve this?
What worked in my case was injecting a org.jboss.seam.mock.HttpServletRequest, since I am using seam in my application. You should try some mock framework like spring.test or mockito.
Here is how my code looks like:
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.jboss.resteasy.plugins.server.resourcefactory.POJOResourceFactory;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.jboss.seam.mock.MockHttpServletRequest;
import org.jboss.seam.mock.DBUnitSeamTest;
public class Test extends DBUnitSeamTest{
#Test
public void test() throws Exception {
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
POJOResourceFactory noDefaults = new POJOResourceFactory(ClasstoBeTested.class); dispatcher.getRegistry().addResourceFactory(noDefaults);
MockHttpRequest request = MockHttpRequest.get("/serviceToBeTested/1961");
MockHttpResponse response = new MockHttpResponse();
HttpServletRequest servletRequest = new MockHttpServletRequest(getSession());
ResteasyProviderFactory.getContextDataMap().put(HttpServletRequest.class, servletRequest);
dispatcher.invoke(request, response);
Assert.assertEquals(HttpServletResponse.SC_OK, response.getStatus());
Assert.assertTrue(response.getContentAsString().contains("1961"));
}
}
I just got hit by this again on another project and decided to investigate once more.
The issue is that in a mock request with Netty, there is no HttpServletRequest available. If you look into the sources of NettyJaxrsServerand related classes, Reasteasy uses its own abstraction for http requests that do not implement HttpServletRequest.
If I change my implementation to use these abstractions, I can access request and response in my resource.
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
#GET
#Path("/")
public void example(#Context HttpResponse response,
#Context HttpRequest request) { ... }
This is not perfect, because it makes my resources depend on Resteasy interfaces but I decided to go with it for now to support mock testing of multipart data.