I'm having the weirdest issue at hand. Having an application secured by spring-seucrity-oauth(2) the authentication suddenly stopped working and we can't find what's going wrong.
Well actually we can. It's the authenticationManager that is injected too late. When configuring the AuthorizationServerEndpointsConfigurer the authenticationManager is not yet injected. For that reason the AuthorizationServerEndpointsConfigurer creates a list of tokenGranters without the ResourceOwnerPasswordTokenGranter.
We have no idea on what could cause this behaviour or how we could solve or work around it.
Any advise or help is appreciated!
AuthorizationServerConfiguration.class:
#EnableAuthorizationServer
#Configuration
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Inject
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore)
.prefix("/api")
.authenticationManager(authenticationManager); // when this method is called authenticationManager is null
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// when I check authentionManager here, it is injected
}
This is how the authenticationManager bean is exposed and created.
SecurityConfigurer.class:
#Configuration
#EnableWebSecurity
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Related
I'm using io.github.lognet:grpc-spring-boot-starter:3.5.3 to add Grpc support. And I have a org.springframework.boot:spring-boot-starter-security dependency. I don't want to add org.springframework.boot:spring-boot-starter-web dependency, cause my application need to use Grpc Netty server without servlets and tomcat server.
Having two implementations of AuthentificationProvider I configured a AuthentificationManager:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final ClientAuthenticationProvider clientAuthenticationProvider;
private final ServiceAuthenticationProvider serviceAuthenticationProvider;
public WebSecurityConfig(ClientAuthenticationProvider clientAuthenticationProvider,
ServiceAuthenticationProvider serviceAuthenticationProvider) {
this.clientAuthenticationProvider = clientAuthenticationProvider;
this.serviceAuthenticationProvider = serviceAuthenticationProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(clientAuthenticationProvider)
.authenticationProvider(serviceAuthenticationProvider);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Build fails with message:
Error:(18, 8) java: cannot access javax.servlet.Filter class file
for javax.servlet.Filter not found
This because WebSecurityConfigurerAdapter needs javax.servlet.Filter.
I'm tried to add javax.servlet:javax.servlet-api:4.0.1 dependency, but application failes at runtime when calling authenticationManager.authenticate(...):
public class MytAuthService {
private final AuthenticationManager authenticationManager;
public AbstractAuthService(
#Qualifier("authenticationManagerBean") AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
public TokenPair authenticate(AuthRequest request) throws AuthenticationException {
...
authenticationManager.authenticate(createAuthToken(username, password));
...
}
with stacktrace:
java.lang.IllegalStateException: This object has not been built at
org.springframework.security.config.annotation.AbstractSecurityBuilder.getObject(AbstractSecurityBuilder.java:55)
~[spring-security-config-5.2.1.RELEASE.jar:5.2.1.RELEASE] at
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:506)
~[spring-security-config-5.2.1.RELEASE.jar:5.2.1.RELEASE] at
org.my.service.auth.MyAuthService.authenticate(MyAuthService.java:75)
~[classes/:?]
When I adding org.springframework.boot:spring-boot-starter-web all works well, but I don't want add Tomcat and servlets to my application.
Can I configure AuthentificationManager to set custom AuthenticationProvider's without extending WebSecurityConfigurerAdapter class or any way? Or may be you can show a good sample/tutorial where Grpc and Spring Security uses only?
Answer myself, I should remove extending WebSecurityConfigurerAdapter and create a AuthenticationManager bean using ProviderManager class.
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
private final ClientAuthenticationProvider clientAuthenticationProvider;
private final ServiceAuthenticationProvider serviceAuthenticationProvider;
public WebSecurityConfig(ClientAuthenticationProvider clientAuthenticationProvider,
ServiceAuthenticationProvider serviceAuthenticationProvider) {
this.clientAuthenticationProvider = clientAuthenticationProvider;
this.serviceAuthenticationProvider = serviceAuthenticationProvider;
}
#Bean
public AuthenticationManager authenticationManagerBean() {
return new ProviderManager(Arrays.asList(clientAuthenticationProvider, serviceAuthenticationProvider));
}
}
In my Spring boot app, I have the following two classes:
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// TODO re-enable csrf after dev is done
.csrf()
.disable()
// we must specify ordering for our custom filter, otherwise it
// doesn't work
.addFilterAfter(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class)
// we don't need Session, as we are using jwt instead. Sessions
// are harder to scale and manage
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
and:
#Component
public class JwtAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
/*
* we must set authentication manager for our custom filter, otherwise it
* errors out
*/
#Override
#Autowired
public void setAuthenticationManager(
AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
}
JwtAuthenticationFilter depends on an AuthenticationManager bean through its setAuthenticationManager method, but that bean gets created in AppSecurityConfig which has JwtAuthenticationFilter autowired in. This whole thing creates a circular dependency.
How should I resolve this issue?
I fixed this issue by following what was suggested here:
Cannot pass AuthenticationManager to custom filter by #Autowired
I removed #Component from JwtAuthenticationFilter and instead of autowiring JwtAuthenticationFilter to WebSecurityConfig class, I defined the bean there:
#Bean
public JwtAuthenticationFilter JwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
I am trying to follow this tutorial on setting up OAuth authentication in my web application I have just started, I am fairly new to spring and have been scratching my head over as to why the OAuth2AuthorisationServerConfig class cannot pick up the bean.
#Configuration
public class ServerSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("JL").password("TEST").roles("USER");
}
#Override
#Bean //Here is the bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/login", "/register").permitAll()
.anyRequest().authenticated()
.and().formLogin().permitAll()
.and().logout().permitAll();
}
}
#Configuration
public class OAuth2AuthorisationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean") // here is the qualifier for bean
private AuthenticationManager authenticationManager;
....
}
The two classes are in the same package
On the OAuth2AuthorisationServerConfig class I had annotated it with #ComponentScan and this seemed to have solved the problem
I have set up the boilerplate spring security Configurer:
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource datasource;
#Override
protected void configure(HttpSecurity http) throws Exception {
// ...setting up security for routes, etc.
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// here I have access to the AuthenticationManagerBuilder
// I can associate it with my datasource, set the password encoder, etc.
JdbcUserDetailsManager userDetailsService = new JdbcUserDetailsManager();
userDetailsService.setDataSource(datasource);
PasswordEncoder encoder = new BCryptPasswordEncoder();
auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
auth.jdbcAuthentication().dataSource(datasource);
}
But what I want is to be able to access that AuthenticationManagerBuilder from another bean like this:
#Service
public class MyUserService {
#Autowired
AuthenticationManagerBuilder builder;
public void createUser(...) {
//use builder here...
JdbcUserDetailsManager userDetailsService = new JdbcUserDetailsManager();
userDetailsService.setDataSource(datasource);
PasswordEncoder encoder = new BCryptPasswordEncoder();
builder.userDetailsService(userDetailsService)
.passwordEncoder(encoder);
builder.jdbcAuthentication().dataSource(datasource);
userDetailsService.createUser(new User(...));
}
Is there any way to access from other beans the same AuthenticationManagerBuilder instance that is automatically passed to the configure() method?
The AuthenticationManagerBuilder is really only meant for building your authentication objects (i.e. UserDetails, AuthenticationProvider, AuthenticationManager). It is not intended to be used within the application itself.
Instead, I would recommend using the UserDetailsManager API. You can create a UserDetailsManager Bean, provide the UserDetailsManager to the AuthenticationManagerBuilder for creating the AuthenticationProvider & AuthenticationManager, then you can use the UserDetailsManager directly within your code.
Something like this:
#EnableWebMvcSecurity
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
...
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, UserDetailsService uds) throws Exception {
auth
.userDetailsService(uds)
.passwordEncoder(new BCryptPasswordEncoder());
}
#Bean
public UserDetailsManager udm(DataSource dataSource) {
JdbcUserDetailsManager udm = new JdbcUserDetailsManager();
udm.setDataSource(dataSource);
return udm;
}
}
#Service
public class MyUserService {
#Autowired
UserDetailsManager udm;
public void createUser(...) {
//use builder here...
udm.createUser(new User(...));
}
}
One thing to note is that we leverage the global instance of AuthenticationManagerBuilder. In order to ensure the configureGlobal method is invoked before building the AuthenticationProvider and AuthenticationManager you need to have EnableGlobalAuthentication, EnableWebMvcSecurity or EnableWebSecurity annotations on the Configuration class (our example already does this).
I created a Spring Boot - Security project based on a example app, however the Rest Controller works fine, means, I can access the rest resource but the security does not seem to fire up at all, so when I added a breakpoint as stated below it does not break there. Not sure why.
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// #formatter:off
auth.inMemoryAuthentication() // breakpoint here
.withUser("roy")
.password("spring")
.roles("ADMIN");
// #formatter:on
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Here is the complete project hosted and editable with Codio: http://bit.ly/1uFI0t5
You have to tell the framework which are the urls that it have to secure.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").access("hasRole('ROLE_USER')")
.and().formLogin();
}
}