spring security java config - multiple authentication manager - java

I'm working on a java spring mvc application. Here is a part of my SecurityConfig class for set AuthenticationManager:
...
#Autowired
private SecurityDAO securityDAO;
...
#Override
protected void configure(AuthenticationManagerBuilder registry) throws Exception {
registry.userDetailsService(securityDAO).passwordEncoder(new BCryptPasswordEncoder());
}
The SecurityDAO is a class that implements UserDetailsService interface.
Now, I need to have two different implementation of UserDetailsService interface. One for admin users with url /admin/login and one for customer with url /customer/login.
I found some samples that implements multiple authentication manager with spring, but all of them use XML config and I can't find an example with java config.
Here is a sample of xml config. In fact, I want to convert this config into java config.

Extend AbstractAutowiringFactoryBean and add your implementation in that class.
It has two methods,doCreateInstance() to know whose object needs to be instantiated and getObjectType() to know which interface implementation is this.
For example
#Configuration
public class CLass extends AbstractAutowiringFactoryBean<Object> {
public enum Env {
devloper, production
}
Env envType = Env.devloper;
#Override
protected Object doCreateInstance() {
switch (envType) {
case devloper:
return new ClassImpl1();
case production:
rreturn new ClassImpl2();
}
throw new RuntimeException("Unsupported implementation type");
}
#Override
public Class<?> getObjectType() {
return SecurityDAO.class;
}
}

Related

SimpUserRegistry doesnot contain any session objects

Iam new to Websockets. I have been trying to use SimpUserRegistry to find session object by Principal. I wrote a custom handshake handler to convert Anonymous users to authenticated users and Iam able to access the Principal name from Websocket session object.
The code for custom handshake handler is shown below
import java.security.Principal;
public class StompPrincipal implements Principal {
private String name;
public StompPrincipal(String name) {
this.name = name;
}
#Override
public String getName() {
return name;
}
}
Handler
class CustomHandshakeHandlerTwo extends DefaultHandshakeHandler {
// Custom class for storing principal
#Override
protected Principal determineUser(
ServerHttpRequest request,
WebSocketHandler wsHandler,
Map<String, Object> attributes
) {
// Generate principal with UUID as name
return new StompPrincipal(UUID.randomUUID().toString());
}
}
But as specified in many questions like this I'am not able to inject the SimpUserRegistry directly.
It throws error
Field simpUserRegistry required a bean of type 'org.springframework.messaging.simp.user.SimpUserRegistry' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.springframework.messaging.simp.user.SimpUserRegistry' in your configuration.
So I created a configuration class as shown below.
#Configuration
public class UsersConfig {
final private SimpUserRegistry userRegistry = new DefaultSimpUserRegistry();
#Bean
#Primary
public SimpUserRegistry userRegistry() {
return userRegistry;
}
}
Now I can autowire and use it but everytime I try to acess the SimpUserRegistry it is empty.
What could be the cause of this problem?
EDIT:
Showing websocket config
#Configuration
#EnableWebSocket
#Controller
#Slf4j
public class WebSocketConfig implements WebSocketConfigurer {
#Autowired
EventTextHandler2 handler;
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
log.info("Registering websocket handler SocketTextHandler");
registry.addHandler(handler, "/event").setHandshakeHandler(new CustomHandshakeHandlerTwo());
}
}
SimpUserRegistry is an "infrastructure bean" registered/provided by Spring WebSocket, you should not instantiate it directly.
Is your WebSocket Spring configuration correct?
Make sure your application is well configured (ie. your configuration class is being scanned).
SimpUserRegistry is imported by spring-messaging dependency: make sure your configuration class is annotated with #EnableWebSocketMessageBroker.
Official documentation: https://docs.spring.io/spring-framework/docs/5.3.6/reference/html/web.html#websocket-stomp-enable
To back the connected users in Redis, you may want to create a new SimpUserRegistry implementation:
public class RedisSimpUserRegistry implements SimpUserRegistry, SmartApplicationListener {
private final RedisTemplate redisTemplate;
public RedisSimpUserRegistry(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
[...]
#Override
public void onApplicationEvent(ApplicationEvent event) {
// Maintain Redis collection on event type
// ie. SessionConnectedEvent / SessionDisconnectEvent
}
[...]
}
PS: The #Controller annotation on your config class is not necessary unless you have an endpoint defined in it.
Edit after new comments:
You can see the DefaultSimpUserRegistry implementation to get an idea of how to do it.
To intercept an application event, you have to implement the ApplicationListener interface (in this case SmartApplicationListener).
The supportsEventType method is important to define which event types you want to intercept:
#Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return AbstractSubProtocolEvent.class.isAssignableFrom(eventType);
}
The AbstractSubProtocolEvent have multiple implementations. The most important ones are SessionConnectEvent, SessionDisconnectEvent.
Intercepting (see onApplicationEvent method) these event types will allow your implementation to maintain the desired state in your Redis cache. You could then store users (ids, etc.).

Spring boot: inject a request-scoped, lazy-initialized custom User object into a controller

I'm building a Spring Boot application to provide a stateless REST API. For security, we're using OAuth 2. My app receives a bearer-only token.
The user's information is stored in our database. I can look it up using the injected Principal in the controller:
#RequestMapping(...)
public void endpoint(Principal p) {
MyUser user = this.myUserRepository.findById(p.getName());
...
}
To avoid this extra line of boilerplate, I would like to be able to inject the MyUser object directly into my controller method. How can I achieve this? (The best I've come up with so far is to create a Lazy, Request-scoped #Bean...but I haven't been able to get it working...)
The Idiomatic Way
The idiomatic way in Spring Security is to use a UserDetailsService or implement your own:
public class MyUserDetailsService implements UserDetailsService {
#Autowired
MyUserRepository myUserRepository;
public UserDetails loadUserByUsername(String username) {
return this.myUserRepository.findById(username);
}
}
And then there are several spots in the Spring Security DSL where this can be deposited, depending on your needs.
Once integrated with the authentication method you are using (in this case OAuth 2.0), then you'd be able to do:
public void endpoint(#AuthenticationPrincipal MyUser myuser) {
}
The Quick, but Less-Flexible Way
It's generally better to do this at authentication time (when the Principal is being ascertained) instead of at method-resolution time (using an argument resolver) as it makes it possible to use it in more authentication scenarios.
That said, you could also use the #AuthenticationPrincipal argument resolver with any bean that you have registered, e.g.
public void endpoint(
#AuthenticationPrincipal(expression="#myBean.convert(#this)") MyUser user)
{
}
...
#Bean
public Converter<Principal, MyUser> myBean() {
return principal -> this.myUserRepository.findById(p.getName())
}
The tradeoff is that this conversion will be performed each time this method is invoked. Since your app is stateless, this might not be an issue (since the lookup needs to be performed on each request anyway), but it would mean that this controller could likely not be reused in other application profiles.
You can achieve this by implementing HandlerMethodArgumentResolver.
For example:
Custom annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface Version {
}
Implementation:
public class HeaderVersionArgumentResolver implements HandlerMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getParameterAnnotation(Version.class) != null;
}
#Override
public Object resolveArgument(
MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest,
WebDataBinderFactory webDataBinderFactory) throws Exception {
HttpServletRequest request
= (HttpServletRequest) nativeWebRequest.getNativeRequest();
return request.getHeader("Version");
}
}
When you implement this you should add this as argument resolver:
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addArgumentResolvers(
List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new HeaderVersionArgumentResolver());
}
}
Now we can use it as argument
public ResponseEntity findByVersion(#PathVariable Long id, #Version String version)

How can I use my custom ConfigurationProperties in custom WebAuthenticationDetails?

In Spring 4.3.x, I have a custom class, call it MyWebAuthenticationDetails that extends WebAuthenticationDetails. I need to use properties in that class that are defined in application.properties. I get those properties via a custom class, called AuthenticationProperties, that uses #ConfigurationProperties. Normally I would autowire in AuthenticationProperties on the class constructor, but that is not possible for MyWebAuthenticationDetails. How can I access properties from within my extension of WebAuthenticationDetails?
Since your MyWebAuthenticationDetails custom details object will be constructed through an AuthenticationDetailsSource bean (which you should have already declared), you can access the AuthenticationProperties as an injected bean thus you will have all your properties accessible.
A simple Java configuration template would be as follows (note that this is not a full functional configuration and aims only to highlight the important configuration entries):
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authenticationDetailsSource(myAuthenticationDetailsSource())/* and all the missiong HTTP configuration*/;
}
#Bean
private AuthenticationDetailsSource<HttpServletRequest, MyWebAuthenticationDetails> myAuthenticationDetailsSource() {
return new MyAuthenticationDetailsSource<HttpServletRequest, MyWebAuthenticationDetails>();
}
private final class MyAuthenticationDetailsSource extends AuthenticationDetailsSourceImpl<HttpServletRequest, MyWebAuthenticationDetails> {
#Autowired
private AuthenticationProperties authenticationProperties;
#Override
public MyWebAuthenticationDetails buildDetails(HttpServletRequest request) {
return new MyWebAuthenticationDetails(request, this.authenticationProperties);
}
}
}

Control the order and logic of how spring security runs the list of AuthenticationProviders

Lets say I have multiple AuthenticationProviders in my Spring application. such as :
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(AAuthenticationProvider());
auth.authenticationProvider(BAuthenticationProvider());
auth.authenticationProvider(CAuthenticationProvider());
}
by default spring security will try these providers in order until one provides a non-null response.
Question: Can I customize this behavior where I can can change the running order and logic of the list providers?
It seems that ProviderManager is somehow responsible of running this logic. would it be possible to override this behavior?
I don't think there is a convenient way to manipulate the order rather than just alter the order sequence in which you add your providers with auth.authenticationProvider.
The builder has an ArrayList that's appened at its end each time you call auth.authenticationProvider. And the evaluation is done in the same order as these providers are added to the list as well.
Hope this helps!
I could not find any direct solution from Spring. I was hoping to find something such as the ability to create custom ProviderManager . My workaround solution is to create one Parent authenticationProvider with one parent UserDetailsService where I can control flow of all UserDetailsServices .
You config class will include the following:
#Configuration
#EnableWebSecurity
public class SecConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService parentUserDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(ParentAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider ParentAuthenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(parentUserDetailsService);
return authenticationProvider;
}
}
The parent service will have access to all child services. so it would look something like this:
#Service
public class ParentUserDetailsService implements UserDetailsService {
#Autowired
UserDetailsService aUserDetailsService;
#Autowired
UserDetailsService bUserDetailsService;
#Autowired
UserDetailsService cUserDetailsService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails user = null;
/* your logic will be here.
You iterate through all of the services
or have some conditional flow. the sky is your limit!
*/
// For Example
if(cond1)
user = aUserDetailsService.loadUserByUsername(username);
else(cond2){
try{
user = bUserDetailsService.loadUserByUsername(username);
}catch(Exception e){
user = cUserDetailsService.loadUserByUsername(username);
}
}
return user;
}
I'm not sure if this is the most optimal solution, but it worked well in my case.

Spring Security 3.2 - configuring global method security to use role hierarchy

Using Spring Security 3.2.5 and Spring 4.1.2, 100% Java config
Our webapp has global method security enabled and service methods annotated with #PreAuthorize - everything is working as expected. I'm trying to add a role hierarchy and having no success at all. Here's the hierarchy I'm trying to achieve:
ROLE_ADMIN can access all methods that ROLE_USER can access.
ROLE_USER can access all methods that ROLE_DEFAULT can access.
Despite my best efforts, a user with ROLE_ADMIN receives a 403 when doing something that results in a call to a method annotated with #PreAuthorized("hasAuthority('ROLE_DEFAULT')")
Here's the relevant configuration code:
AppInitializer
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
#Override
protected Class<?>[] getRootConfigClasses()
{
return new Class[]
{
AppConfig.class, SecurityConfig.class
};
}
#Override
protected Class<?>[] getServletConfigClasses()
{
return new Class[]
{
MvcConfig.class
};
}
// other methods not shown for brevity
}
AppConfig.java
#Configuration
#ComponentScan(basePackages={"myapp.config.profile", "myapp.dao", "myapp.service", "myapp.security"})
public class AppConfig
{
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth,
AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> detailSvc) throws Exception
{
PreAuthenticatedAuthenticationProvider authProvider = new PreAuthenticatedAuthenticationProvider();
authProvider.setPreAuthenticatedUserDetailsService(detailSvc);
auth.authenticationProvider(authProvider);
}
// other methods not shown for brevity
}
SecurityConfig.java
#Configuration
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
PKIAuthenticationFilter pkiFilter = new PKIAuthenticationFilter();
pkiFilter.setAuthenticationManager(authenticationManagerBean());
http.authorizeRequests()
.antMatchers("/app/**").fullyAuthenticated()
.and()
.anonymous().disable()
.jee().disable()
.formLogin().disable()
.csrf().disable()
.x509().disable()
.addFilter(pkiFilter)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(WebSecurity web) throws Exception
{
// ignore everything but /app/*
web.ignoring().regexMatchers("^(?!/app/).*");
}
}
MvcConfig.java
#Configuration
#EnableWebMvc
#ComponentScan({"myapp.controller"})
public class MvcConfig extends WebMvcConfigurerAdapter
{
// resource handlers, content negotiation, message converters configured here
}
In the same package as SecurityConfig (so it is thus part of the AppConfig component scan) I had this class:
GlobalMethodSecurityConfig.java
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class GlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration
{
#Bean
public RoleHierarchy roleHierarchy()
{
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER > ROLE_DEFAULT");
return roleHierarchy;
}
#Bean
public RoleVoter roleVoter()
{
return new RoleHierarchyVoter(roleHierarchy);
}
#Bean
#Override
protected AccessDecisionManager accessDecisionManager()
{
return new AffirmativeBased(Arrays.asList(roleVoter()));
}
// The method below was added in an attempt to get things working but it is never called
#Override
protected MethodSecurityExpressionHandler createExpressionHandler()
{
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setRoleHierarchy(roleHierarchy());
return handler;
}
}
In another attempt I made AppConfig extend GlobalMethodSecurityConfiguration but a user with ROLE_ADMIN cannot call a method requiring ROLE_DEFAULT access.
I'm sure I've misconfigured something somewhere but I can't figure out where I've gone wrong despite reading everything I can find on configuring global method security with a role hierarchy. It appears this would be trivial using XML configuration but the Java config solution eludes me.
I'd override GlobalMethodSecurityConfiguration#accessDecisionManager method. You can see source code that RoleVoter uses.
Here is my suggested overridden source code.
#Override
protected AccessDecisionManager accessDecisionManager() {
var roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_SUPER > ROLE_ADMIN");
var expressionHandler = (DefaultMethodSecurityExpressionHandler) getExpressionHandler();
expressionHandler.setRoleHierarchy(roleHierarchy);
var expressionAdvice = new ExpressionBasedPreInvocationAdvice();
expressionAdvice.setExpressionHandler(expressionHandler);
return new AffirmativeBased(List.of(
new RoleHierarchyVoter(roleHierarchy),
new PreInvocationAuthorizationAdviceVoter(expressionAdvice),
new AuthenticatedVoter(),
new Jsr250Voter()
));
}
Since this question keeps getting views I thought I'd post a follow-up to it. The problem appears to be with the line
roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER > ROLE_DEFAULT");
I don't remember why I wrote the hierarchy like that but it's not correct. The API for that method handles the same situation thusly:
Role hierarchy: ROLE_A > ROLE_B and ROLE_B > ROLE_C.
Directly assigned authority: ROLE_A.
Reachable authorities: ROLE_A, ROLE_B, ROLE_C.
Eventually it became clear that a hierarchical model didn't fit our roles so we instead implemented a finer-grained set of authorities mapped to roles, as mentioned in the Spring Security Reference:
For more complex requirements you may wish to define a logical mapping between the specific access-rights your application requires and the roles that are assigned to users, translating between the two when loading the user information.

Categories

Resources