Spring security - Access to a controller method based on an attribute - spring

I'm configuring Spring Security across all my controllers.
I want some method executions to start only when "my system is enabled". This information is accessible from all over the controllers via a specific static method (I can make it non-static).
My point is that I want to avoid making an explicit check in java code at the beginning of every method.
How can I get there via Spring Security?

One approach is to use a handler interceptor.
Here is general idea:
(1) Configure url patterns which you want to block:
<util:list id="sysEnableCheckUrlPatterns" value-type="java.lang.String">
<beans:value>/module1/**</beans:value>
<beans:value>/module2/**</beans:value>
</util:list>
(2) Write an interceptor:
public class SysEnableCheckInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
/*
If system enabled then return true. Otherwise return false (and optionally write something in response)
*/
}
}
(3) Configure that interceptor. In 3.1 you can do it as follows:
#Configuration
public class AppConfig extends WebMvcConfigurerAdapter {
#Resource(name="sysEnableCheckUrlPatterns")
/* or use #Autowired or #Inject if you like */
private String[] sysEnableCheckUrlPatterns;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SysEnableCheckInterceptor()).addPathPatterns(sysEnableCheckUrlPatterns);
}
}

You can use SPEL (Spring Expression Language) in a security annotation.
See http://static.springsource.org/spring-security/site/docs/3.0.x/reference/el-access.html

Related

Add more field into header using interceptor of spring boot not work

I'm using spring boot.
I want to add a field into header of every response. So that, i using interceptor. The code is:
#Component
public class ApiVersionInterceptor extends HandlerInterceptorAdapter{
private final Logger log = LoggerFactory.getLogger(ApiVersionInterceptor.class);
#Autowired
private Environment environment;
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception arg3) throws Exception {
String apiVersion = environment.getProperty(ApiVersion.VERSION_KEY.getKey());
log.debug("api-version:"+apiVersion);
response.addHeader("Api-Version", apiVersion);
}
}
And the configuration is:
#Configuration
public class InterceptorsConfiguration extends WebMvcConfigurerAdapter {
#Autowired
private ApiVersionInterceptor apiVersionInterceptor;
/**
* Add interceptor
*/
#Override
public void addInterceptors(final InterceptorRegistry registry) {
//Add api-version field to header of response
registry.addInterceptor(apiVersionInterceptor);
}
}
To make sure this snipped code is run because of:
2017-12-06 02:35:10,392 DEBUG [] [http-nio-8080-exec-7] ApiVersionInterceptor: api-version:1.9.0
But i don't understand, i don't see this field in the header of any response.
Update
My app use Restful webservice, so don't have view phase.
Thanks for help.
You should add header in a earlier phase, override the preHandle method in your ApiVersionInterceptor. Because in afterCompletion response is already committed and skip header changes.

SpringBoot HandlerInterceptor not intercepting library endpoint

I have a SpringBoot app where I have implemented a HandlerInterceptor to log general information about API usage. I want it to also log requests to Spring Security's OAuth2 endpoint but it does not intercept the request.
#Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
// register the interceptor that will write API usage info to a file
registry.addInterceptor(new ServiceUsageInterceptor());
}
How can I configure the HandlerInterceptor to intercept all requests?
Thanks
This turned out to be unrelated to the interceptor. The usage was being written to a log file using a custom AccessLogValve in the embedded Tomcat. Updating the pattern seemed to resolve the issue.
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
TomcatEmbeddedServletContainerFactory factory = (TomcatEmbeddedServletContainerFactory) container;
CustomAccessLogValve accessLogValve = new CustomAccessLogValve();
accessLogValve.setEnabled(true);
// set pattern
accessLogValve.setPattern("timestamp=\"%t\" local_host=\"%v\" status=\"%s\" remote_host=\"%h\" client_id=\"%q\" uri=\"%r\" execution_time=\"%D\"");
factory.addContextValves(accessLogValve);
}
}

Control #RestController availability programmatically

Is it possible to control a #RestController programmatically to enable it or disable it? I don't want to just write code in each #RequestMapping method to do some kind of if (!enabled) { return 404Exception; }
I've seen this question but that works only at startup time. What I need is really something that would allow me to enable or disable the controller multiple times.
I've thought of different ways but don't know which are doable in spring.
Actually control the container (jetty in my case) so requests to that particular endpoint are disabled
Somehow control RequestMappingHandlerMapping since it seems to be that class that does the mapping between urls and controllers
control the lifecycle of the #RestController component so that i can create it and destroy it at will, but then i'm not sure how to trigger the mapping to the endpoint
If the end result is that you want to respond with a 404 when you decide that a specific endpoint should be disabled then you could write an interceptor which checks whether your enabled condition is false and, if so, sets the response accordingly.
For example:
#Component
public class ConditionalRejectionInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String requestUri = request.getRequestURI();
if (shouldReject(requestUri)) {
response.setStatus(HttpStatus.NOT_FOUND.value());
return false;
}
return super.preHandle(request, response, handler);
}
private boolean shouldReject(String requestUri) {
// presumably you have some mechanism of inferring or discovering whether
// the endpoint represented by requestUri should be allowed or disallowed
return ...;
}
}
In Spring Boot, registering your own interceptor just involves implementing a WebMvcConfigurerAdapter. For example:
#Configuration
public class CustomWebMvcConfigurer extends WebMvcConfigurerAdapter {
#Autowired
private HandlerInterceptor conditionalRejectionInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
// you can use .addPathPatterns(...) here to limit this interceptor to specific endpoints
// this could be used to replace any 'conditional on the value of requestUri' code in the interceptor
registry.addInterceptor(conditionalRejectionInterceptor);
}
}

How to designate public endpoints using custom annotation in spring rest controllers?

We are using OAuth2 for securing our REST endpoints. Nearly all of our endpoints require authentication. We have couple of public endpoints. We configure spring security using #EnableWebSecurity. All the public endpoints are explicitly listed in the configuration (see "publicpath_x" in the example below). Instead of explicitly adding each new public enpodint in the configuration, it would be much easier to have a custom annotation, e.g. #PublicAccess which will designate each public endpoint. Is it possible to configure that endpoints annotated with this annotation will be considered as public, i.e. no authentication will be required? We don't want to designate public endpoints in path (e.g. all public endpoints path will start/end with "/public").
Security configuration:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//...
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatchers("publicpath1", "publicpath2").permitAll() //...
}
}
Example public REST controller with custom annotation:
#RestController
public class PublicController1 {
#PublicAccess //our custom annotation
#RequestMapping(value = "publicpath1", method = RequestMethod.GET)
public void publicEndpoint1() {
//...
}
}
I was trying the following classes with no success.
javax.servlet.Filter
org.springframework.web.servlet.handler.HandlerInterceptorAdapter
You can use the #PreAuthorize Annotations as method security
Detail see here

Custom default headers for REST API only using Spring Data REST

I have a use case where my application hosts REST API and web application and we need to add custom header to REST APIs only. REST APIs are enabled through Spring Data REST. Typically we could use Servlet Filter to achieve this but we need code the logic of isolating requests to our REST API and add the custom headers. It would be nice if Spring Data REST API allows to add a default header to all the responses it generates. What are your thoughts? Don't say I am lazy :)
For folks looking for actual implementation details..
Interceptor
public class CustomInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("adding CORS headers.....");
response.addHeader("HEADER-NAME", "HEADER-VALUE");
return true;
}
}
Java Configuration
#Configuration
public class RepositoryConfig extends
RepositoryRestMvcConfiguration {
#Override
public RequestMappingHandlerMapping repositoryExporterHandlerMapping() {
RequestMappingHandlerMapping mapping = super
.repositoryExporterHandlerMapping();
mapping.setInterceptors(new Object[] { new CustomInterceptor() });
return mapping;
}
}
As Spring Data REST is built on top of Spring MVC, the easiest way is to configure a custom HandlerInterceptor as described in the reference documentation.
With Spring Data REST the easiest way is to extend RepositoryRestMvcConfiguration and override repositoryExporterHandlerMapping, call the parent method and then invoke ….setInterceptors(…) on it.
Finally I managed to make the setup of custom interceptor working also on spring-data-rest 2.4.1.RELEASE.
#Configuration
public class RestMvcConfig extends RepositoryRestMvcConfiguration {
#Autowired UserInterceptor userInterceptor;
#Autowired ApplicationContext applicationContext;
#Override
public DelegatingHandlerMapping restHandlerMapping() {
RepositoryRestHandlerMapping repositoryMapping = new RepositoryRestHandlerMapping(resourceMappings(), config());
repositoryMapping.setInterceptors(new Object[] { userInterceptor }); // FIXME: not nice way of defining interceptors
repositoryMapping.setJpaHelper(jpaHelper());
repositoryMapping.setApplicationContext(applicationContext);
repositoryMapping.afterPropertiesSet();
BasePathAwareHandlerMapping basePathMapping = new BasePathAwareHandlerMapping(config());
basePathMapping.setApplicationContext(applicationContext);
basePathMapping.afterPropertiesSet();
List<HandlerMapping> mappings = new ArrayList<HandlerMapping>();
mappings.add(basePathMapping);
mappings.add(repositoryMapping);
return new DelegatingHandlerMapping(mappings);
}
}
I had to override the restHandlerMapping method, copy-paste it's content and add a line repositoryMapping.setInterceptors for adding custom interceptor, in my case the UserInterceptor.
Is there any better way?

Resources