I am trying my hands on a simple jersey application for few simple RESTful APIs. I want to try role based securities over my API endpoints, but not sure how?
Should the annotation be declared over WebService class like below?
#Slf4j
#Path("/account")
#Autherization
#DeclareRoles("ADMIN", "STUDENT", "TEACHER")
public class AccountService extends SpringApplication {
private static Logger logger = Logger.getLogger("AuthenticationService");
#Path("/greet")
#GET
#Produces(MediaType.TEXT_PLAIN)
#PermitAll
public Response greet(){
logger.info("Welcome to Tarkshala Scholar Account APIs");
return getBean(AuthenticationServiceHandler.class).greet();
}
}
or should it be declared on Filters?
So Jersey already handles the authorization for you with its RolesAllowedDynamicFeature. Just register it in your Jersey ResourceConfig. There is no need for #DeclareRoles, as that is an EJB mechanism. Jersey has its own separate system for handling roles.
What happens under the hood is that there is a ContainerRequestFilter (annotated with #Priority(Priorities.AUTHORIZATION)) that checks your resource methods to see what roles are allowed, then it gets the SecurityContext from the ContainerRequestContext and called the SecurityContext#isUserInRole(String role) method for each role declared in the #RolesAllowed (should there be any). If the SecurityContext doesn't pass the check, Jersey throws a ForbiddenException.
So with that said, what you need to do for your authentication is create a ContainerRequestFilter (annotated with #Priority(Priorities.AUTHENTICATION), and in that filter do your authentication, then create a SecurityContext with all the roles for Jersey to check. This method separates the concerns; one filter for authentication (which we created) and one filter for authorization (which Jersey provides for us.
You can see an example of an authentication filter that uses Basic Authentication in this post.
Related
I am new to Spring Boot Security. I am performing validation of licenseKey in every end-point in REST call. It is working fine.
I want to do it in a common way like SecurityConfig extends WebSecurityConfigurerAdapter {} class so that I should not pass extra parameter in methods for validation. It means there should be common validation if the licenseKey exists then the REST calls should be authorized to go otherwise, it should throw error. Currently I am passing HttpServletRequest which contains licenseKey, the methods are working fine. But our requirement is to perform only in one place in SecurityConfig so that all the requests can be validated.
#GetMapping(path="some/path")
public ResponseEntity<> viewDetails(HttpServletRequest httpRequest, MappingVO mappingVO) {
String licenseUser = userDetailsService.getLicenseUser(httpRequest).getUser().getEmailAddress();
....
....
}
#DeleteMapping(path="some/path")
public ResponseEntity<> deletePart(HttpServletRequest httpRequest, Part part) {
String licenseUser = userDetailsService.getLicenseUser(httpRequest).getUser().getEmailAddress();
....
....
}
In class CustomUserDetails, it has been written like this.
public CustomUserDetails getLicenseUser(HttpServletRequest httpRequest) {
String userId = httpRequest.getHeader("licenseKey");
CustomUserDetails ud = (CustomUserDetails) loadUserByUsername(userId);
return ud;
}
You should add a custom filter in the filter chain in your security config that executes before each request.
Just create a Custom Filter implementing OncePerRequestFilter.java and do the license Key validation inside of that filter. The implementation logic inside your Custom filter will run once before each request that is made on your spring boot server.
Refer
https://www.baeldung.com/spring-onceperrequestfilter
https://www.javadevjournal.com/spring-boot/spring-boot-add-filter/
If you are using Spring Security and want to filter requests to verify that it has valid token passed to it (if you're not already doing this), refer to the official documentation of your respective version of spring security to see where should you add your filter inside the filterChain.
Check Filter Ordering in:
https://docs.spring.io/spring-security/site/docs/3.1.4.RELEASE/reference/security-filter-chain.html
The token validation filter should ideally be exeucted before UsernamePasswordAuthenticationFilter.
In spring boot project I have a controller which has an endpoint with #PreAuthorize annotation
#RestController
#RequestMapping("/path")
class SomeController {
#PostMapping
#PreAuthorize("hasAuthority('SOME_AUTHORITY')")
public ResponseEntity<Object> doSomething() {
}
}
And also I have a filter that extends the OncePerRequestFilter
public class AuthTokenFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain f) throws IOException, ServletException {
//here I want to get the authorities specified in the controllers' endpoints
}
}
The question is how to get controllers' endpoints' authorities specified by the #PreAuthorize annotation in the above filter?
I recommend to move hasAuthority('SOME_AUTHORITY') into separate method in spring bean and use that method in filter and in #PreAuthorize annotation. In that way you will have common place for validate logic and you will keep code consistency as well.
Let me know if you need code examples.
You can do this in Spring Security's way, by using Pointcuts similar to AuthorizationMethodPointcuts. AuthorizationManagerBeforeMethodInterceptor reads these annotations using this pointcut.
GitHub link to spring-security for reference.
https://github.com/spring-projects/spring-security/tree/main/core/src/main/java/org/springframework/security/authorization
A filter dynamically intercepts requests and responses to transform or use the information contained in the requests or responses. Filters typically do not themselves create responses, but instead provide universal functions that can be "attached" to any endpoint.
OncePerRequestFilter . Spring guarantees that the OncePerRequestFilter is executed only once for a given request.
In your case you are extending OncePerRequestFilter in AuthTokenFilter and as you mentioned in comment you are authorizing user in this filter if user have valid details (in this case setting Authentication object).
Once you will hit your endpoint Spring will execute AuthTokenFilter logic
If details will valid then AuthTokenFilter will set Authentication in Spring context.
Spring Security supports authorization at the method level with help of different type of annotation like #PreAuthorize, #Secured . But enable annotation based security, we need to add the #EnableGlobalMethodSecurity annotation on any #Configuration class. example :
#Configuration
#EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
public class MethodSecurityConfiguration {
//default configuration class
}
HttpSecurity is tied to URL endpoints while #PreAuthorize is tied to controller methods and is actually located within the code adjacent to the controller definitions.
authorities/roles should set in Authentication object inside AuthTokenFilter (in your case) and those role can validated with #PreAuthorize annotation.
#PreAuthorize will trigger after AuthTokenFilter filter
Short and sweet:
I want to be able to filter incoming requests to authenticate the user then take the roles defined in my database and use them in the Jersey 2.5.1 Service classes.
e.g.
#RolesAllowed("Custom1", "Custom2")
#Post
.....
Currently I have the following, which seems to work with the basic #PermitAll and #DenyAll annotations, I am just not sure how to overload/what to overload to get some custom code working with Jersey 2.5.1. I've seen examples for Jersey1. Should I just create a request Filter and do it in there and set the securityContext? Any help would be appreciated.
public class TestApi extends ResourceConfig {
public TestApi() {
super(AuthenticateResource.class);
register(RolesAllowedDynamicFeature.class);
}
}
Figured out my problem. Injected Resource Info then pulled out the annotation. This works if it's not pre-matching
#Context
private ResourceInfo resourceInfo;
Annotation[] annotations = resourceInfo.getResourceMethod().getDeclaredAnnotations();
SecurityContext is either set by the underlying container or it's set manually in your application (usually in ContainerRequestFilter). If your container is sophisticated enough to set the correct security context for you (with correct principal) you can go this way. Otherwise create a custom ContainerRequestFilter similar to the one in Jersey example ContainerAuthFilter.
Environment: JBoss AS 7, RestEasy 3.0.5.
I would like to use a preprocess filter in my application to authenticate a particular user and add this authenticated user to the request scope. I tried the following:
#Provider
public class SecurityFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) {
// authenticate, lookup user code etc...
requestContext.setProperty("User", new User("test"));
}
}
And then access it at the bean level using:
#Stateless
public class TestBean {
#Context HttpServletRequest servletRequest;
#GET
#Path("/hello")
public String hello() {
return "Hello " + servletRequest.getAttribute("User");
}
}
However the Context elements are not injected when the bean is annotated as stateless (stateless is required for EJB logic) due to a 2 year old RestEasy bug.
Can anyone think of a solution to this issue?
Alternatively, is there a more typical solution that prepopulating these objects using a filter?
Kind thanks for reading.
What I have done before is that I have a web application, "facade" if you want, to the EJBs. The JAX-RS layer is implemented in this application. A servlet filter intercepts the request to the JAX-RS resources and calls a CDI service that implements the "authenticate, lookup user code etc" logic. On success, the CDI service exports the current user object to CDI. This user object is #Inject'ed in the EJBs.
If CDI is an option for you (and I don't see why not in this setup), you could do the same (i.e. export the User to CDI) from your SecurityFilter, without having to create a web-app.
Additionally the dependency of the service layer (EJBs) to web-specific APIs (HttpServletRequest) is a bit creepy; I really think dependency injection of the actual User object is the way to go.
Let say we have an API endpoint configured using Spring MVC and Spring Security. We would like to be able to handle pairs of #RequestMapping and #Secured annotations where the only #Secured annotation values differ from pair to pair. This way, we would be able to return a different response body depending on security rules for the same request.
This may allow our code to be more maintainable by avoiding to check for security rules directly into the method body.
With a not working example, here is what we would like to do :
#Controller
#RequestMapping("/api")
public class Controller {
#Secured ({"ROLE_A"})
#RequestMapping(value="{uid}", method=RequestMethod.GET)
#ResponseBody
public Response getSomething(#PathVariable("uid") String uid) {
// Returns something for users having ROLE_A
}
#Secured ({"ROLE_B"})
#RequestMapping(value="{uid}", method=RequestMethod.GET)
#ResponseBody
public Response getSomethingDifferent(#PathVariable("uid") String uid) {
// Returns something different for users having ROLE_B
}
}
How can we achieve this ?
And if this can be done: How the priority should be managed for a user who has both ROLE_A and ROLE_B ?
Assuming you are using Spring 3.1 (or up) together with the RequestMappingHandlerMapping (and RequestMappingHandlerAdapter) you can extend the request mapping mechanism. You can do this by creating your own implementation of the RequestCondition interface and extend the RequestMappingHandlerMapping to construct this based on the #Secured annotation on your method.
You would need to override the 'getCustomMethodCondition' method on the RequestMappingHandlerMapping and based on the Method and the existence of the #Secured annotation construct your custom implementation of the RequestCondition. All that information is then taken into account when matching incoming requests to methods.
Related answers (although not specific for #Secured annotations but the mechanism is the same) is also to be found here or here
I don't think you can do this in spring-mvc, since both routes have exactly the same #RequestMapping (#Secured) is not taken into account by the route engine of spring-mvc. The easiest solution would be to do this:
#Secured ({"ROLE_A", "ROLE_B"})
#RequestMapping(value="{uid}", method=RequestMethod.GET)
#ResponseBody
public Response getSomething(#PathVariable("uid") String uid, Principal p) {
// Principal p gets injected by spring
// and you need to cast it to check access roles.
if (/* p.hasRole("ROLE_A") */) {
return "responseForA";
} else if (/* p.hasRole("ROLE_B") */) {
return "responseForB";
} else {
// This is not really needed since #Secured guarantees that you don't get other role.
return 403;
}
}
However, I would change your design, since the response is different per role, why not have 2 separate request mappings with slightly different URLs? If at some point you have users with role A and B at the same time, you can't let the user choose what response to get (think, for example, of the public and private profiles of LinkedIn)