It is possible to allow only mobile devices to access REST API on my server side (Based on spring framework) ?
I just found a solution, don't say that is the best way but anyway it works. For that I'm using spring-mobile library
Create a new Interceptor, to check device type and know to allow him to access API or not, code:
public class MobileDeviceInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Device d = new LiteDeviceResolver().resolveDevice(request);
if(!d.isNormal()) {
return true;
}
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return false;
}
}
In configuration class add it:
public class AppConfig extends WebMvcConfigurerAdapter {
...
#Bean
public MobileDeviceInterceptor deviceTypeInterceptor(){
return new MobileDeviceInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(deviceTypeInterceptor());
}
}
Test it right now and it works.
Note You have to know if User-Agent headers was changed on non mobile platform into mobile values for example:
android
ipad
silk
...
This interceptor will allow access.
Related
i'm trying to use cache (caffeine) with Spring boot and im having a problem. I need to check the header "header-name" in every call but application is caching it so after first request with the right header, dont matter what header i send and the application will not check it and is just returning data from the cache, is there anyway that i can force spring to check header and then get data from cache?
#GetMapping("/request-a")
#Cacheable(cacheNames = "cachename", key = "#root.methodName")
public ResponseEntity<?> makeRequest(#RequestHeader("header-name") String headerName) {
this.authConfig.headerCheck(headerName);
/*
code
*/
}
I already used header "Cache-Control:no-cache" and didnt resolve my problem.
Thanks in advance.
Edit1: method "headerCheck" just check if its equal to another String or not null.
Found a solution:
Create a classe that implements HandlerInterceptor and use preHandle method.
#Component
public class CheckHeaderInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// validate what you want, on error return false
// if everything its ok, return true
}
}
Then register the handler with:
public class WebMvcConfig implements WebMvcConfigurer {
#Autowired
private CheckHeaderInterceptor interceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor).addPathPatterns("url that you wannna use handler");
}
}
I am thinking what would be best solution for following case. Suppose we have at start CRUD app - using Spring Boot. I would like to add read only state for this application - which allows only data read and blocks create, update, delete data operations for admin role. I think about adding aspect (#Aspect) which checks current app state (which is saved in db) and starts if create, update, update operations are invoked. If app is in read-only state - exception will be thrown (handled by #ControllerAdvice)
I wonder if adding aspect is the best option - I dont want modify existing code. Whats your take on that? Moreover - how would you write integration tests for #aspect - testing if aspect starts properly? How could be aspects tested for this case? What are good practises for testing #aspects (integration tests #springboottest)
This honestly seems like an inconvenient way of doing this. Why not just add an Interceptor that checks for this? I did something similar before
#Component
#RequiredArgsConstructor
public class ReadOnlyModeInterceptor implements HandlerInterceptor {
private final ServerProperties serverProperties;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (serverProperties.isReadOnlyMode()) {
String method = request.getMethod();
boolean isReadOnlyMethod = "GET".equals(method) || "OPTIONS".equals(method);
String servletPath = request.getServletPath();
boolean isReadOnlyPath = isReadOnlyPath(servletPath);
if (!isReadOnlyMethod && isReadOnlyPath) {
throw new ServiceUnavailableException("Server is in read-only mode.");
}
}
return true;
}
private boolean isReadOnlyPath(String servletPath) {
if (serverProperties.isFullyReadOnly()) {
return true; // wildcard option, entire server is read-only
}
return serverProperties.getReadOnlyPaths().stream().anyMatch(servletPath::contains);
}
}
You also need to register it
#RequiredArgsConstructor
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final ReadOnlyModeInterceptor readOnlyModeInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(readOnlyModeInterceptor).order(0);
}
}
I have a requirement where using spring security I want that if a request of any type contains a particular header with a particular value then only it should be allowed to access the api's otherwise not. Below is my configuration code:
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter{
public AppSecurityConfig() {
}
#Autowired
public void configure​(HttpSecurity http) throws Exception {
http.authorizeRequests().requestMatchers(new CustomHeaderRequestMatcher()).permitAll();
}
}
Below is the customer header request matcher business logic:
public class CustomHeaderRequestMatcher implements RequestMatcher{
public CustomHeaderRequestMatcher() {
}
#Override
public boolean matches(HttpServletRequest request) {
if(Objects.nonNull(request.getHeader("my-token"))
&& request.getHeader("my-token").equals("abc")) {
System.out.println("true");
return true;
}
System.out.println("false");
return false;
}
}
But here I could see that even when I am not passing the custom header "my-token" in my request it is allowing to access the api. For every request though custom request matcher class is being called. I am confused as to why spring security is not handling the case?
you have not blocked all other requests. spring sees the first rule and doesn't know what to do next. you could add a "anyRequest().denyAll()" or "anyRequest().authenticated()" after the "permitAll()"
I am creating a Restlet based service that matches an incoming request with a specific initial path. In the ServerResource that handles match I want access to the remaining path.
Router parentRouter;
parentRouter.attach("/svc/", new MyService());
parentRouter.attach("/svc/1.0/", new MyService());
public class MyService extends Router
{
public MetricsService()
{
attach("/m/", MyResource.class).setMatchingMode(Template.MODE_STARTS_WITH);
}
}
public class MyResource extends ServerResource
{
#Override
protected void doInit() throws ResourceException
{
String remainingPath = ??? <-- how do I do this?
}
#Get
protected void doit() throws ResourceException
{
...
}
}
Now for example the following requests result in the following remaining paths:
http://localhost/svc/m/ --> ""
http://localhost/svc/m/a --> "a"
http://localhost/svc/v1.0/m/some/m/path --> "some/m/path"
Of course there are always workarounds to get this fixed, but there is likely a more elegant of retrieving this using the existing Restlet framework. Specifically since Routers are already aware of remaining paths.
Any help or suggestions appreciated.
After some fiddling i found the answer:
#Override
protected void doInit() throws ResourceException
{
String remainingPath = getReference().getRemainingPart();
}
I'd like to implement versioning in my RESTful web service API. I intend to put the version into the URL, viz.: /api/v3/endpoint
What is the ideal way to do this (in Java)?
Although this irritates my aversion to manual version control, my best guess is to save the API interface into a new file and include a bunch of comments to defend against too much entropy:
/** Do not leave more than 2 previous versions in existence! **/
#Path("/api/v3")
public interface RestfulAPIv3
{
int version = 3;
#Path("/resources")
#Method(GET)
public Response getResources();
}
Of course the idea would be not to copy the implementation also, but to allow it to support multiple versions. This might require moving identical signatures forward to the newer versions so no collisions would happen across interfaces in the class file:
public class RestfulAPIImpl implements RestfulAPIv3, RestfulAPIv2
{
public Response getResources()
{
List<Resource> rs = HibernateHelper.getAll(Resource.class);
// Can we do something with v2/v3 diffs here?
}
#Deprecated
public Response getOptions() // ONLY in v2!
{
return HibernateHelper.getOptions();
}
}
Thinking it through, I have no idea how we'd know which version of an endpoint the client has called, except maybe forwarding the request into the methods which is not my favorite thing.
So, my question is - what have all the versioned API implementers been doing to keep all this stuff from getting out of hand? What's the best way to do this? Am I on the right track?
(Note: this other question is about the 'if' - my question is about the 'how'.)
An alternative to not passing forward a parameter specifying the version number is to add an annotation to the method so that it automatically captures that information and saves it on a request object that can be read elsewhere.
Taking into account that your API might have requests with parameters that differ amongst versions and also have responses that look different you might have to have multiple controllers and view-model classes, one for each version of the API.
UPDATE
As per request, follows some sample code (I've used Play Framework 2.4).
So the objective is to achieve something like this in a controller class:
#Versioned(version = 0.1)
public Result postUsers() {
// get post data
UsersService service = new UsersService(getContext());
service.postUsers(postData);
// return result
}
And like this in a service class:
public class UsersService extends Service {
public UsersService(RequestContext context) {
super(context);
}
public ReturnType postUsers() {
double apiVersion = getContext().getAPIVersion();
// business logic
}
}
In order to accomplish that, you would have a Versioned annotation:
import java.lang.annotation.*;
import play.mvc.With;
#With(VersioningController.class)
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Versioned {
double version();
}
And a VersioningController:
import play.libs.F.Promise;
import play.mvc.*;
public class VersioningController extends Action<Versioned> {
public final static String API_VERSION = "version";
#Override
public Promise<Result> call(Http.Context context) throws Throwable {
context.args.put(API_VERSION, configuration.version());
return delegate.call(context);
}
}
And a RequestContext to help you manage that (you could also use the request context to manage the request timestamp, the user requesting the operation, etc):
public class RequestContext {
private double version;
public RequestContext(Double version) {
setAPIVersion(version);
}
public double getAPIVersion() {
return version;
}
public void setAPIVersion(double version) {
this.version = version;
}
}
Moreover, your controllers could have a GenericController from which they all extend:
import play.api.Play;
import play.mvc.*;
import play.mvc.Http.Request;
public abstract class GenericController extends Controller {
protected static RequestContext getContext() {
return new RequestContext(getAPIVersion());
}
protected static double getAPIVersion() {
return (double) Http.Context.current().args
.get(VersioningController.API_VERSION);
}
}
And an abstract Service from which all service classes extend:
public abstract class Service {
private RequestContext context;
public Service(RequestContext context) {
setContext(context);
}
public RequestContext getContext() {
return context;
}
public void setContext(RequestContext context) {
this.context = context;
}
}
Having all that said, keep in mind that it could be better to try to isolate the API versioning in as few layers as possible. If you can keep your business logic classes from having to manage API versions it's all the better.