how override spring framework beans? - spring

I want to customize some of the codes of OAuth authorization server provided by spring security. the code responsible for generating /oauth/authorize is a bean named AuthorizationEndpoint. in AuthorizationServerEndpointsConfiguration class the following code creates a bean of AuthorizationEndpoint class:
#Bean
public AuthorizationEndpoint authorizationEndpoint() throws Exception {
AuthorizationEndpoint authorizationEndpoint = new AuthorizationEndpoint();
FrameworkEndpointHandlerMapping mapping = getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
authorizationEndpoint.setProviderExceptionHandler(exceptionTranslator());
authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
authorizationEndpoint.setTokenGranter(tokenGranter());
authorizationEndpoint.setClientDetailsService(clientDetailsService);
authorizationEndpoint.setAuthorizationCodeServices(authorizationCodeServices());
authorizationEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
authorizationEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
authorizationEndpoint.setUserApprovalHandler(userApprovalHandler());
return authorizationEndpoint;
}
I want to override it by a new custom bean. I have created a class which extends AuthorizationEndpoint. for now I have pasted the same code inside this new class.
public class AuthorizationEndpointCustom extends AuthorizationEndpoint {
creating the bean:
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
AuthorizationServerEndpointsConfiguration asec;
#Bean
// #Order(value = Ordered.LOWEST_PRECEDENCE)
#Primary
public AuthorizationEndpoint authorizationEndpoint () {
AuthorizationEndpointCustom authorizationEndpoint = new AuthorizationEndpointCustom();
FrameworkEndpointHandlerMapping mapping = asec.getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
authorizationEndpoint.setProviderExceptionHandler(asec.getEndpointsConfigurer().getExceptionTranslator());
authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
authorizationEndpoint.setTokenGranter(asec.getEndpointsConfigurer().getTokenGranter());
authorizationEndpoint.setClientDetailsService(clientDetailsService);
authorizationEndpoint.setAuthorizationCodeServices(asec.getEndpointsConfigurer().getAuthorizationCodeServices());
authorizationEndpoint.setOAuth2RequestFactory(asec.getEndpointsConfigurer().getOAuth2RequestFactory());
authorizationEndpoint.setOAuth2RequestValidator(asec.getEndpointsConfigurer().getOAuth2RequestValidator());
authorizationEndpoint.setUserApprovalHandler(asec.getEndpointsConfigurer().getUserApprovalHandler());
return authorizationEndpoint;
}
private String extractPath(FrameworkEndpointHandlerMapping mapping, String page) {
String path = mapping.getPath(page);
if (path.contains(":")) {
return path;
}
return "forward:" + path;
}
when I try to create a bean of this new class I encounter the following error:
APPLICATION FAILED TO START
Description:
The bean 'authorizationEndpoint', defined in
org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration,
could not be registered. A bean with that name has already been
defined in class path resource
[com/example/demo/AuthorizationServerConfig.class] and overriding is
disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting
spring.main.allow-bean-definition-overriding=true
the error goes away by adding the suggested config to application.properties. but the new bean does not replace the framework bean. in another part of my code I accessed the AuthorizationEndpoint from applicationContext. I called the .getClass() of this object and it is the same bean from the framework:
"org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint"
how can I force spring to use my bean?

You need a Configuration class
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class AppConfig {
#Bean
public AuthorizationEndpoint authorizationEndpoint() {
if(...) return new AuthorizationEndpoint();
else return new AuthorizationEndpointCustom();
}
}

I red an article about overriding beans and it seems so messy and unpredictable. read here
it's best to avoid doing so. The solution to disable framework bean lies in excluding the configuration class which creates it. but this means we have to implement the hole thing ourselves.
#SpringBootApplication(exclude=<AuthorizationServerEndpointsConfiguration>.class)
but the solution to overriding the framework endpoints is much easier.all we have to do is create a controller with mapping for /oauth/authorize
Customizing the UI Most of the Authorization Server endpoints are used
primarily by machines, but there are a couple of resource that need a
UI and those are the GET for /oauth/confirm_access and the HTML
response from /oauth/error. They are provided using whitelabel
implementations in the framework, so most real-world instances of the
Authorization Server will want to provide their own so they can
control the styling and content. All you need to do is provide a
Spring MVC controller with #RequestMappings for those endpoints, and
the framework defaults will take a lower priority in the dispatcher.
In the /oauth/confirm_access endpoint you can expect an
AuthorizationRequest bound to the session carrying all the data needed
to seek approval from the user (the default implementation is
WhitelabelApprovalEndpoint so look there for a starting point to
copy). You can grab all the data from that request and render it
however you like, and then all the user needs to do is POST back to
/oauth/authorize with information about approving or denying the
grant. The request parameters are passed directly to a
UserApprovalHandler in the AuthorizationEndpoint so you can interpret
the data more or less as you please. The default UserApprovalHandler
depends on whether or not you have supplied an ApprovalStore in your
AuthorizationServerEndpointsConfigurer (in which case it is an
ApprovalStoreUserApprovalHandler) or not (in which case it is a
TokenStoreUserApprovalHandler). The standard approval handlers accept
the following:
read more here.
there is another question related to this subject: read here

Related

Overriding a class defined in Spring Boot \ Spring Security

We are in the process of migrating a legacy application to Spring Boot. In order to continue with testing until we have assigned roles to users, I would like to override the following:
class: SecurityContextHolderAwareRequestWrapper
method: public boolean isUserInRole(String role)
I have created a new class which extends SecurityContextHolderAwareRequestWrapper and overrides isUserInRole(), as follows:
#Component
public class MySecurityContextHolderAwareRequestWrapper extends org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper {
public MySecurityContextHolderAwareRequestWrapper(HttpServletRequest request,
AuthenticationTrustResolver trustResolver, String rolePrefix) {
super(request, trustResolver, rolePrefix);
}
#Override
public boolean isUserInRole(String role) {
return true;
}
When the application is run, the new bean does not take the place of the existing SecurityContextHolderAwareRequestWrapper class. This is clear because when the new class is instantiated, the constructor is not injected with the beans being injected into SecurityContextHolderAwareRequestWrapper. The application fails to start because parameters of type AuthenticationTrustResolver and String to the new class MySecurityContextHolderAwareRequestWrappercould could not be found
What is the correct way to override SecurityContextHolderAwareRequestWrapper, or for that matter any class in the Spring Boot framework?
Thanks
The SecurityContextHolderAwareRequestWrapper class is ultimately used by the SecurityContextHolderAwareRequestFilter configured with http.servletApi(). Some information about this feature is available in the Spring Security reference docs.
This feature shields you from direct dependence on Spring Security and provides very high level integration with Spring Security through the Servlet API. You cannot directly influence the class used to wrap the request.
However, if you wish to temporarily modify the result of role checks, you can influence what roles are available in the Authentication object during authentication itself. See info in the docs on GrantedAuthority, and note that you will want to customize roles during authentication by providing a custom UserDetailsService.

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.

Spring Proxy Creation of Classes annotated with #Configuration or #Component

Spring uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. If a class is annotated with #Configuration, then CGLIB is used.
However, one limitation of Spring AOP is that once the call has finally reached the target object, any method calls that it may make on itself are going to be invoked against the this reference, and not the proxy. This piece of information is important to remember when using #Transactional and in other places as well.
So having that knowledge, in the code below, is Spring injecting the actual instance or the proxy of SimpleBean?
#Configuration
public class Config {
#Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
#Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean()); //<---
}
}
And what is the behavior if a class is annotation with #Component?
Let me give you another perspective.
Say there is an another bean AnotherBeanConsumer that also needs a simpleBean. Simple Bean has a Singleton scope:
#Configuration
public class Config {
#Bean
public SimpleBean simpleBean() {
return new SimpleBean();
}
#Bean
public SimpleBeanConsumer simpleBeanConsumer() {
return new SimpleBeanConsumer(simpleBean());
}
#Bean
public AnotherBeanConsumer anotherBeanConsumer() {
return new AnotherBeanConsumer(simpleBean());
}
}
Now the question is, how its possible that two calls to simpleBean() made from different methods simpleBeanConsumer and anotherBeanConsumer return the same instance of the simple bean (since its a singleton obviously)?
IMO (and disclaimer, I'm not affiliated with spring or something), This is the main reason of creating proxies that wrap Configurations.
Now indeed Spring AOP has a limitation of calling methods just as you've stated, however who said that spring under-the-hood uses spring AOP? The bytecode instrumentation done on much lower levels doesn't have a limitation like this. After all creating a proxy means: "create a proxy object that will have the same interface but will alter the behavior", right?
For example if you use CGLIB that uses inheritance you could create a proxy out of configuration that looks like this (schematically):
class CGLIB_GENERATED_PROXY extends Config {
private Map<String, Object> singletonBeans;
public SimpleBean simpleBean() {
String name = getNameFromMethodNameMaybePrecached();
if(singletonBeans.get(name) != null) {
return singletonBeans.get(name);
}
else {
SimpleBean bean = super.simpleBean();
singletonBeans.put(name, bean);
return bean;
}
}
....
}
Of course its only a schematic picture, in real life there is an application context that basically provides the access to the map like this, but you get the point.
If its not enough, then there are some even more sophisticated frameworks that spring must make use of in order to load a configuration (like ASM)...
Here is an example:
If you use #ConditionalOnClass(A.class) and the class doesn't really exist in runtime, how spring can load the bytecode of the configuration that uses this configuration and not fail on something like NoClassDefFoundException?
My point is that it goes far beyond the spring AOP, and has its quirks :)
Having said that, nothing that I've describe above requires the real components to be always wrapped in Proxies of any kind. So in the most trivial case, when SimpleBean does not by itself have some annotations that require proxy generation (stuff like #Cached, #Transactional and so forth), Spring won't wrap the object of that type and you'll get a plain SimpleBean object.

Use multiple ClientAuthentiation with spring-vault

We have an application using spring-vault. It authenticates to Vault using an AppRole. We use the token we get from that operation to read and write secrets. The configuration for the VaultEndpoint and AppRoleAuthentication are auto-configured from a properties file.
Code looks like this:
#Autowired
private ApplicationContext context;
#Autowired
private VaultOperations vault;
private Logger logger = LoggerFactory.getLogger(VaultFacade.class);
public VaultFacadeImpl() {
logger.debug("Creating VaultFacade with autowired context");
context = new AnnotationConfigApplicationContext(VaultConfig.class);
vault = context.getBean(VaultTemplate.class);
//vault variable ready to use with vault.read or vault.write
//in our VaultFacadeImpl
}
I would like to keep autowire capabilities, but also support two other ClientAuthentication implementations:
The existing TokenAuthentication
A custom ClientAuthentication implementation (LDAP auth backend)
The end result would be having two authentication mechanism available at the same time. Some operations would be carried out with the application's credentials (AppRole in Vault), others with the user's credentials (LDAP in Vault).
I think I can create multiple AbstractVaultConfiguration classes, each returning a different ClientAuthentication derivative. But how can I create a VaultTemplate for configuration class?
If you want to have an additional VaultTemplate bean, then you need to configure and declare the bean yourself. You can keep the foundation provided by AbstractVaultConfiguration. Your config could look like:
#Configuration
public class CustomConfiguration {
#Bean
public VaultTemplate ldapAuthVaultTemplate(ClientFactoryWrapper clientHttpRequestFactoryWrapper,
ThreadPoolTaskScheduler threadPoolTaskScheduler) {
return new VaultTemplate(…,
clientHttpRequestFactoryWrapper.getClientHttpRequestFactory(),
ldapSessionManager(threadPoolTaskScheduler));
}
#Bean
public SessionManager ldapSessionManager(ThreadPoolTaskScheduler threadPoolTaskScheduler) {
ClientAuthentication clientAuthentication = new MyLdapClientAuthentication(…);
return new LifecycleAwareSessionManager(clientAuthentication,
threadPoolTaskScheduler,
…);
}
}
On the client side (using the second VaultTemplate) you need to make sure to look up the appropriate instance. Spring doesn't limit you to a bean per type but allows registration of multiple beans of the same type.

Setting up Custom HandlerExceptionResolver in Spring MVC

I am trying to override WebMvcConfigurerAdapter.configureHandlerExceptionResolvers() and provide my own ExceptionHandlerExceptionResolver to Spring MVC. The motive behind this is to provide Custom Content Negotiation in such a way that if the user requests for any garbage in "Accept" header, I can return him a JSON response with "media not supported exception". I was partially able to acheive the configuration using the bellow setup.
#Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager(new ErrorContentNegotiation());
ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver = exceptionHandlerExceptionResolver();
exceptionHandlerExceptionResolver.setContentNegotiationManager(contentNegotiationManager);
exceptionHandlerExceptionResolver.afterPropertiesSet();
exceptionResolvers.add(exceptionHandlerExceptionResolver);
}
#Bean
public ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver() {
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(getHttpMessageConverter());
ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver = new ExceptionHandlerExceptionResolver();
exceptionHandlerExceptionResolver.setMessageConverters(messageConverters);
return exceptionHandlerExceptionResolver;
}
public class ErrorContentNegotiationStrategy implements ContentNegotiationStrategy {
#Override
public List<MediaType> resolveMediaTypes(final NativeWebRequest webRequest) {
return Lists.newArrayList(Globals.JSON);
}
}
I am getting this exception when the spring starts up.
No qualifying bean of type [org.springframework.web.accept.ContentNegotiationStrategy] is defined: expected single matching bean but found 2: errorContentNegotiationStrategy,mvcContentNegotiationManager
Doesn't work when I add a #Qualifier annotation to my ErrorContentNegotiationStrategy class and give it a unique name. Throws the same Exception.
If I remove #Compoenent annotation and leave the code as is, then ErrorContentNegotiationStrategy() method in ErrorContentNegotiaionStrategy is not getting called.
Did anyone face this issue ?
Add the #Primary annotation to your ErrorContentNegotiationStrategy class:
Indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one 'primary' bean exists among the candidates, it will be the autowired value.
This should at least solve the exception during startup.
After debugging the issue, I found that I was trying to load 2 beans of same type ( which is what the error message says. One of the bean was actual implementation and other one was a Mock for test case. Since both resided in the same package #component scanned the base package and couldn't decide which one to load. I resolved the issue by using #profile, which helps in loading beans based on the profile you load. I used 2 profiles, one for testing and one for development.

Resources