I have somewhere around 70 of API's of which most of them are secured by following token based approach. There are (let's say 20) open api which do not need any kind of token.
For authentication of secured apis I initially though of implementing it in spring filters and skip this authentication for open api's. Something like below
#Component
class AccountFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) {
if(request.getRequestURI().contains.(list of open apis)) {
chain.doFilter(request, response);
return;
} else {
/*
* logic for token validation
*
}
}
}
But sooner found it becoming tedious. I mean putting if else for 20 apis makes code look cluttered.
Eventually I thought of creating a custom annotation something like #Authenticator and implement the token validation logic there. Put this annotation on RestController methods of secured api's and omit it from open api's.
#RentionPolicy
#Target
#interface Authenticator {
}
#RestController
public class TestController {
#Authenticator
#GetMapping(/api/v1/securedapi)
public void testSecurily() {
}
// Omit #Authenticator from non secured api
#GetMapping(/api/v1/api)
public void test() {
}
}
Unfortunately, I could not find anything which helps me. All I can find is spring security but that is not in my scope as of now.
I know spring supports custom annotations but I could not find anything which fulfils my requirement.
Can any one help me with this?
Also I want to know
If my approach is right? If yes how can i proceed further?
If not how can I achieve this functionality without cluttering my code.
Related
I am implementing a mechanism for replacing short links.
I need to forwarded request to another controller. I found examples how to do it in spring on models, but I don't understand how to do it in RestControllers
Example what i found (use models)
#Controller
public class ShrotLinkForwardController {
#RequestMapping("/s/*")
public String myMethod(HttpServletRequest request) {
return "forward:/difmethod";
}
}
Or maybe I'm looking in the wrong direction and I need to make a filter?
UPD. I don't know the final endpoint, it is calculated in the forwarded method. So, i cant autowired other controller
There are 2 ways to achieve what you want.
1. Call the method on the target controller directly.
Controllers are just normal Spring beans. You can get it via autowire.
#Controller
public class ShrotLinkForwardController {
#Autowired
OtherController otherController;
#RequestMapping("/s/*")
public String myMethod(Model model) {
otherController.doStuff();
return ...;
}
}
2. Trigger the forward by returning a string
To trigger the forward, try returning a String instead of ModelAndView.
This is the approach you mentioned in your question. Note that the syntax should be forward:/forwardURL. The string after forward: is the URL pointing to another controller, not the method name.
#Controller
public class ShrotLinkForwardController {
#RequestMapping("/s/*")
public String myMethod(Model model) {
return "forward:/forwardURL";
}
}
you could inject the target controller and simply call the method
#Controller
public class ShortLinkForwardController {
#Autowired
private RestController target;
#RequestMapping("/s/*")
public String myMethod(HttpServletRequest request) {
return target.myMethod(request);
}
}
Caveat: Path related request properties will still point to "/s/*"
Or use ResponseEntity and set target location...
public ResponseEntity<Void> myMethod(HttpServletRequest request) {
return ResponseEntity.status(302).location(URI.create(...)).build();
}
All answers are about returning String
But I've found another solution
Maybe it will help someone with my problem in case when you need to make forward from one REST endpoint to another REST endpoint.
And it also could be applied to your case.
#RestController
public class CustomerController {
#GetMapping("/forwarding_endpoint")
public void makeForward(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getSession().getServletContext().getRequestDispatcher("/forward_endpoint").forward(request, response);
}
}
UPD. I don't know the final endpoint, it is calculated in the
forwarded method. So, i cant autowired other controller
but I don't understand how to do it in RestControllers
I can see some indications of possible bad design here, so I will try to explain the possible issues and how should be handled according to best practices.
If your requirement is to make a forward to another controller, then this might be an indication of 3 possible issues:
The job to be done by the other controller (which you say you want to forward to) can be extracted into a service method in service layer. Then both controllers can call the same service method, without each controller be aware of the other.
Your need could also be an indicator of the following issue. You need 2 controllers for exactly the same practical reason, so that they provide for same input exactly the same output, but to be available from 2 different URLs. If this is the case then you can use just 1 controller and allow it to be executed for both URLs. See the following code to achieve this:
#RequestMapping({"/s/*", "/s2/*})
public String myMethod(HttpServletRequest request) {
return "some response";
}
You need to expose only 1 URL to the client which will serve everything. Then the approach with forward will also not benefit you, since the client will be able to reach the other forwarded controller directly if he wishes so. In this case you can implement 1 single controller which then according to the needs builds different responses. You can do this in RestController although not suggested by Sonar and other code review tools by marking the method to return ResponseEntity<?>. Example:
#RequestMapping("/s/*")
public ResponseEntity<?> myMethod(HttpServletRequest request) {
if (condition 1) {
return new ResponseEntity<YourObject1>(HttpStatus.OK);
} else if (condition 2) {
return new ResponseEntity<YourObject2>(HttpStatus.OK);
} else {
return new ResponseEntity<YourObject3>(HttpStatus.OK);
}
}
this last choice is not considered best practice with <?> but for this requirement I don't see any other way out.
I want to redirect users to a different page after login depending on what type of user they are, and if they're on a mobile device. My project uses Spring MVC and Spring Security. To get the redirect logic working, I use a Spring Security AuthenticationSuccessHandler to identify the type of user post-login and direct them to the correct page.
I was hoping to use Spring Mobile to check at this point whether they're on a mobile device, and if so send them to the mobile version of the URL. However, it seems that interceptors such as DeviceResolverHandlerInterceptor get executed after the Spring Security filters, and so the resolution hasn't happened at the point I need it to.
At the moment, the only solution I can see to this is to disable the interceptor and instead directly use LiteDeviceResolver's resolveDevice() method. This feels a little bit hacky, but I can't see another obvious choice.
Is there any better way to configure this people are aware of?
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Bean
public DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor() {
return new DeviceResolverHandlerInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(deviceResolverHandlerInterceptor()).order(Ordered.HIGHEST_PRECEDENCE);
}
}
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
boolean isMobile = DeviceUtils.getCurrentDevice(request) != null
&& DeviceUtils.getCurrentDevice(request).isMobile();
...
I have spring boot application which exposes REST endpoints protected by spring security.
I need to restrict access to some paths depending on service call. Let's say I have a service like this:
#Service
public class AccessService {
boolean hasAccess(String requestedPath) {
// some business logic here
}
}
The service will check user roles, some business conditions and return true or false.
Now I need to integrate this service call into my security configuration.
So far I have configuration like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.and().authorizeRequests()
.anyRequest().hasRole("USER");
}
I see no way of adding the service call here (as it is completely static).
What I'm trying:
Currently I'm thinking of overriding my AuthenticationProvider and extending it with the additional functionality.
The other option would be to extend my REST controllers from a class which would do some sort of authorization, but I'm not sure if it is possible.
Question: How can I protect REST endpoints based on service method call? What is the proper way of doing that?
This is explained in the reference guide. Basically you need to use the access expression instead of the hasRole. You can then write powerful security expressions.
Something like the following should do the trick:
anyRequest()
.access("#accessService.hasAccess(request.requestURI) && hasRole('USER')");
This restricts access to user with the role ROLE_USER and which have access according to your own custom logic.
I think a good way to to this is to use #PreAuthorize
Some documentation can be found here: Expression-Based Access Control.
You are also able to add your own evaluator class/methods to customize to your specific needs:
#PreAuthorize("#customPermissionEvaluator.accessMethod(variable)")
Example class:
#Service(value = "customPermissionEvaluator")
public class CustomPermissionEvaluatorImpl implements CustomPermissionEvaluator {
#Override
public boolean accessMethod(int variable) {
if (variable == 1) {
return true;
}
return false;
}
}
I have the following problem:
I have a rest API on a jooby server. I want to create a custom annotation interceptor which handles particular requests and validates the oauth token in the header.
#GET
#Path("current")
#AuthenticationTokenValidator
public Result getCurrentUser(final Request req) {
...
Or for an entire controller
#Path("/v1/some_route")
#Consumes("json")
#Produces("json")
#AuthenticationTokenValidator
public class SomeController {
How can I do that? Thanks in advance!
You need a filter and then ask for route attributes. Something similar to this:
{
use("*", (req, rsp, chain) -> {
String value = req.route().attr("authenticationTokenValidator");
// your code goes here
});
}
Not sure if the annotation at the class level is supported.
Checkout the documentation about route attributes, there is a similar example.
We want to implement a public RESTful API integrated in our software (written in java) that might be used by various clients to build small e-commerce apps (e.g. for Android or iPhone). This API includes getting a list of products, categories, shopping cart support, etc.
We need to provide an API that will allow user registration and couple of other sensitive functions. How should we protect this API against spam and bruteforcing? In the standard product we use reCAPTCHA. Any alternative for the REST counterpart?
First, think of separation of concerns. What is the purpose of REST API?
A REST API should do offer a service to the client. Client sends a request via REST protocol, and gets a response for its request. In code, this looks something like:
#GET
public Response getClientInfo(#QueryParam("clientId") Integer clientId) {
ClientDTO clientDTO = database.getClientInfo(clientId);
return ResponseWrapper.wrap(clientDTO);
}
Now, you want your REST method doing ONLY this and nothing else. Otherwise, you would put block-bruteforce-and-spam-logic in your REST method and you would get a mess of the code that is not extensible, hard to version, etc. If you want to change your, e.g. blacklisting policy you would have to change each and every REST method, and it's bulky. If you want to check the calls before the make it to REST methods, then take a look at Filters. Every request and response pass through a chain of filters and could be check for misuse of the server.
I don't know what is your technology stack is, but I would suggest looking into these:
JBoss AS7.
DeltaSpike (enables you powerful Interceptors that will check user rights and execution rights before the execution of the REST method).
for example:
#LoggedInUser
#GET
public Response getClientInfo(...) {
...
}
This security annotation #LoggedInUser (which, by the way, you define) will give sign to an Interceptor to check this security constraint, e.g.
#Secures (built in annotation)
#LoggedInUser
public boolean hasRight(Identity identity) {
return identity.isLoggedIn(); //or if he is in certain group of users
}
Context and Dependency Injection context (used in DeltaSpike).
JBoss Filters (a filter chain where you can create your own filter that, for example, checks if some IP is trying to send multiple calls within a very short period ~ 10 lines of code).
An example of the Filter
#Startup
#ApplicationScoped
#Filter(around= "org.jboss.seam.web.ajax4jsfFilter")
public class IPTrackerFilter extends AbstractFilter {
//IPTracker is your #ApplicationScoped bean that remembers all IP addresses accessing the application.
#Inject
private IPTracker fIPTracker;
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
if (!(req instanceof HttpServletRequest)) {
chain.doFilter(req, res);
return;
}
final String ipAddress= ((HttpServletRequest)req).getRemoteAddr();
if (fIPTracker.isBlackListed(ipAddress)) {
//implement error message here
sendErrorMessage(response);
return;
} else {
//all good, continue
chain.doFilter(req, res);
}
}
}
PS. I gave you the link for DeltaSpike, for others is really easy to find. Also, if you find DeltaSpike to obscure, try with JBoss Seam Security Framework.