What does the #Secure annotation do and what package is it apart of - jersey

I'm writing an API using Java EE, JAX-RS, Jersey. In doing this I've implemented my own security context and security filter.
Looking at questions like this one (How to get MIME type of uploaded file in Jersey) I've seen the #Secure annotation but what does it do? My hope was that is was an annotation that queries the isSecure method of the security context in the same way that #RolesAllowed does for checking if a user has the right to access a particular method. If so is there such a way of doing so with annotations or am I stuck to using the #Context to get the security context and just from that.

The #Secure annotation seems to be a custom one. JAX-RS/Jersey does not support such feature out-of-the-box but it's not that hard to implement. Lets say you have your own #Secure annotation and you want to do checks whether a communication channel is secure for methods annotated with this annotation. You need to create a custom ResourceFilterFactory in which you'll assign a special filter for such methods:
public class IsSecureResourceFilterFactory implements ResourceFilterFactory {
private class IsSecureFilter implements ResourceFilter, ContainerRequestFilter {
// ResourceFilter
#Override
public ContainerRequestFilter getRequestFilter() {
return this;
}
#Override
public ContainerResponseFilter getResponseFilter() {
return null;
}
// ContainerRequestFilter
#Override
public ContainerRequest filter(final ContainerRequest request) {
// Check whether the channel is secure.
if (request.isSecure()) {
return request;
}
// Throw an exception if it's not.
throw new WebApplicationException(Response.Status.FORBIDDEN);
}
}
#Override
public List<ResourceFilter> create(final AbstractMethod abstractMethod) {
// Add IsSecureFilter for resource methods annotated with #Secure annotation (ignore other resource methods).
return abstractMethod.isAnnotationPresent(Secure.class)
? Collections.<ResourceFilter>singletonList(new IsSecureFilter()): null;
}
}
Now you need to tell Jersey about this ResourceFilterFactory. There are 2 ways:
via web.xml
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>my.package.IsSecureResourceFilterFactory</param-value>
</init-param>
or via META-INF/services mechanism - you need to create a file called META-INF/services/com.sun.jersey.spi.container.ResourceFilterFactory which would contain a fully qualified name of your factory (in this case my.package.IsSecureResourceFilterFactory) and make sure this file is on the class-path of your application.

Related

Spring authorization at package level; AOP?

we have a spring boot app with a java package that has spring controllers with endpoints for admin-like functionality. right now they all start with the same request mapping.
so one way i could do authorization of every endpoint in the package is by WebSecurityConfigurerAdapter implementation...
http.authorizeRequests()
.antMatchers("/admin/**").access("hasRole('ADMIN')")
but i was thinking it would be nice to use AOP somehow to target the package of the admin controllers to PreAuthorize all controller methods in the package. just in case someone on the project creates a new controller in the proejct with a different request mapping it would automatically be projected. also, if we decided to PreAuthorize at the #Service level instead of the controller level then this way could be used as well.
so, is it possible to PreAuthorize at the package level with AOP or some other way?
Spring provides default AOP interceptor for #Secured and #PreAuthorized annotations, but it works only on class or method level, and AFAIK not intended to be expanded to package level.
To put it simply, Spring intercepts calls to certain methods or all class methods with these annotations and check whether SecurityContextHolder holds Authority object and whether its collection of GrantedAuthority matches any of the annotation's value field values. So, you can do the same thing using AOP, for example like this:
#Aspect
#Component
public class AdminServiceAOPAuthorization {
private static final List<String> ALLOWED_ROLES = List.of("ADMIN", "SUPER_ADMIN");
#Pointcut("within(com.example.service.admin.*)") // <- any method in any class of the package
public void adminServiceLayer() {}
#Before("adminServiceLayer()")
public void authorize(JoinPoint jp) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
// throw some exception depending on the logic
}
boolean authorized = authentication.getAuthorities().stream()
.anyMatch(ga -> ALLOWED_ROLES.contains(ga.getAuthority()));
if (!authorized) {
throw new AccessDeniedException("Access denied");
// log or whatever
}
}
}
For better performance I'd advise to use it at the service layer to let Spring create proxy using implemented interface (if you use them, of course) instead of proxying controller class.

Spring Boot role validation controller using aspect

I have several controller functions separated by role, and instead of doing role validation in each controller method, I found that it seems to be able to get done by using Aspect, however something isn't right in my implementation as the code in Aspect never runs
Annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface ForMerchantOnly {}
Aspect:
#Aspect
#Configuration
public class ForMerchantOnlyAspect {
private static final Logger logger = LogManager.getLogger(ForMerchantOnlyAspect.class);
#Before("#annotation(com.example.api.annotation.ForMerchantOnly) && args(request)")
public void before(HttpServletRequest request) throws ServiceException {
if (!(request instanceof HttpServletRequest)) {
throw new RuntimeException("request should be HttpServletRequesttype");
}
String domain = request.getServerName();
System.out.println("Aspect showing domain " + domain);
// -- other code
}
}
Controller
#ForMerchantOnly
#GetMapping("/list")
public ResponseEntity<ApiResp> list() {
System.out.println("Show something");
return ResponseEntity.ok().body();
}
I'm assuming when i call controller /list method via chrome browser, it would hit the code in ForMerchantOnlyAspect but it just went into the controller method directly. Am I missing something?
The Aspect was not working as it could not find a matching joinpoint . There are no controller methods that has annotation #ForMerchantOnly and has an argument of type HttpServletRequest
From the documentation :
args: Limits matching to join points (the execution of methods when
using Spring AOP) where the arguments are instances of the given
types.
Following aspect may be used for the requirement . Scoping designator within will set the scope to advice.
#Before("#annotation(com.example.api.annotation.ForMerchantOnly) && within(com.example.api..*)")
public void before() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest();
System.out.println("Aspect showing domain " + request.getServerName());
}
Also note that an Aspect is better annotated with #Component and #Configuration be used for configurations.
You may also have a look at Method Security of Spring security framework , which lets to secure a method with annotations.
From the documentation
From version 2.0 onwards Spring Security has improved support
substantially for adding security to your service layer methods. It
provides support for JSR-250 annotation security as well as the
framework’s original #Secured annotation. From 3.0 you can also make
use of new expression-based annotations. You can apply security to a
single bean, using the intercept-methods element to decorate the bean
declaration, or you can secure multiple beans across the entire
service layer using the AspectJ style pointcuts.

How are components managed in Spring MVC and how to inject a customized component in Spring 3.2.5?

I’m considering to replace the DefaultSessionAttributeStore implementation of Spring MVC 3.2.5 with some class of my own, and I’ve known from the source code that in my 3.2.5 spring source, it’s SessionAttributesHandler which possesses a SessionAttributeStore interface reference and invokes the session store function. My question is how to replace that by DI? The SessionAttributesHandler holds a final private sessionAttributeStore reference and can only be set by the constructor:
public class SessionAttributesHandler {
...
private final SessionAttributeStore sessionAttributeStore;
...
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null.");
this.sessionAttributeStore = sessionAttributeStore;
SessionAttributes annotation = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class);
if (annotation != null) {
this.attributeNames.addAll(Arrays.asList(annotation.value()));
this.attributeTypes.addAll(Arrays.<Class<?>>asList(annotation.types()));
}
for (String attributeName : this.attributeNames) {
this.knownAttributeNames.put(attributeName, Boolean.TRUE);
}
}
...
}
Are all the components of spring mvc managed in the spring DI container? How to inject my own SessionAttributeStore implementation into SessionAttributesHandler? What does the "Class handlerType" argument mean in the constructor? From source, it seems like it's the "controller" class. Since SessionAttributesHandler is invoked and held by a ModelFactory, and in ModelFactory there is no code instantiating the SessionAttributesHandler, is there any "XML" bean configuration file for the Spring MVC inner components and how to overwrite them?
If you want to provide your own implementation of a SessionAttributeStore you need to manually configure the RequestMappingHandlerAdapter and set your custom implementation on there. That will take care of using it through-out the rest of the infrastructure.
Assuming that you use java config you can do the following
#Configuration
public class MyConfiguration extend WebMvcConfigurationSupport {
#Bean
public SessionAttributeStore sessionAttributeStore() {
return new MyCustomSessionAttributeStore();
}
#Override
#Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter rmha = super.requestMappingHandlerAdapter();
rmha.setSessionAttributeStore(sessionAttributeStore());
return rmha;
}
}
If you want to do this in XML you either have to write a BeanPostProcessor which sets it on the default RequestMappingHandlerAdapter instance created by <mvc:annotation-driven /> or configure it manually and drop the namespace support.

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?

#Endpoint and #Transactional on the same class using Spring-ws library

I am trying to implement a web-service endpoint which would be transactional because I don't want to create a special "worker" class with transactional methods. I'm using Spring-ws library together with Spring framework.
Here is my class definition:
#Endpoint
#Transactional
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS)
public class MyEndpoint implements ApplicationContextAware { ... }
Notice that I explicitly specified proxying method to force using CGLIB.
Also notice that my class implements some interface(s), so by default Spring uses JDK dynamic proxy unless another proxying method is specified. This kind of proxies is not appropriate in my case.
The problem begins on application deployment when PayloadRootAnnotationMethodEndpointMapping class starts working. It collects names of all Spring beans with #Endpoint annotation. My endpoint class is counted twice with names "myEndpoint" and "scopedTarget.myEndpoint". This duplication causes ApplicationContextException with message "Cannot map endpoint [...] on registration key [...]: there's already endpoint [...] mapped".
Question: how can I make my endpoint class being transactional?
You might write your own PayloadRootAnnotationMethodEndpointMapping extension and override the initApplicationContext method. There you can check for the scopedTarget. prefix to filter out unwanted beans:
public class ProxiedBeanAwareEndpointMapping extends PayloadRootAnnotationMethodEndpointMapping {
#Override
protected void initApplicationContext() throws BeansException {
initInterceptors();
String[] beanNames = getApplicationContext().getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
if (!beanName.startsWith("scopedTarget.")) {
Class<?> endpointClass = getApplicationContext().getType(beanName);
if (endpointClass != null && AnnotationUtils.findAnnotation(endpointClass, getEndpointAnnotationType()) != null) {
registerMethods(beanName);
}
}
}
}
}
Or you can use the open session in view approach so you don't need to proxy your #Endpoints.

Resources