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();
}
Related
I have 2 class that extends WebSecurityConfigurerAdapter, one with #Order(1) the other with #Order(2).
I want the first one to filter my APIs starting with /user/** and the other to filter /client/** so I can access localhost:8080/client (to have a login page from Salesforce, then it will allow the client to acess some other /client API).
I have :
#EnableWebSecurity
#Configuration
#Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private OpenLdapAuthenticationProvider openLdapAuthenticationProvider;
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private JwtRequestFilter jwtRequestFilter;
public WebSecurityConfig(OpenLdapAuthenticationProvider openLdapAuthenticationProvider,
JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint,
JwtRequestFilter jwtRequestFilter) {
this.openLdapAuthenticationProvider = openLdapAuthenticationProvider;
this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
this.jwtRequestFilter = jwtRequestFilter;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(openLdapAuthenticationProvider);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// CORS
httpSecurity.cors().and().antMatcher("/**").csrf().disable().sessionManagement();
httpSecurity
.csrf().disable().headers().frameOptions().deny()
.and()
.authorizeRequests().antMatchers("/user/login").permitAll()
.antMatchers("/user/**").authenticated()
.and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// JWT Filter
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
And the second configuration :
#EnableWebSecurity
#Order(2)
#Configuration
public class WebSecurityConfigSF extends WebSecurityConfigurerAdapter {
#Autowired
MyOAuth2UserService myOAuth2UserService;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// Configuration du CORS
httpSecurity.cors().and().antMatcher("/**").csrf().disable().sessionManagement();
httpSecurity.csrf().disable().headers().frameOptions().deny()
.and().authorizeRequests().antMatchers("/client/**","/client").authenticated()
.and()
.oauth2Login();
}
}
What happens is that when I call /user it works (can access all /user APIs), but when I try /client it doesn't (error Oauth2).
When I switch order, I can access localhost8080:/client and log in into Salesforce no problem but all /user APIs are not filtered, meaning I can access them without any JWT in the header.
Is what I want even possible ?
I have spring boot app with thymeleaf. I am using spring security formLogin method for security and now I need to add JWT for only some APIs.
#EnableWebSecurity
public class SecurityConfigurations {
#Autowired
UserDetailsServiceImpl userDetails;
#Bean
DaoAuthenticationProvider provider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(encoder());
provider.setUserDetailsService(userDetails);
return provider;
}
#Bean
PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
#Configuration
#Order(1)
public class JWTSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
DaoAuthenticationProvider provider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(provider);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.authorizeRequests().antMatchers("/api/user/authenticate").permitAll()
.antMatchers("/api/user/**").hasRole("USER")
.and().
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
#Configuration
public static class FormLoginConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
DaoAuthenticationProvider provider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(provider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/admins**").hasAnyRole("SADMIN").antMatchers("/admin/**")
.hasAnyRole("ADMIN", "SADMIN", "WADMIN").antMatchers("/rest/**")
.hasAnyRole("ADMIN", "SADMIN", "WADMIN", "USER").antMatchers("/user/**").hasAnyRole("USER")
.anyRequest().permitAll().and().formLogin().loginPage("/sign-in-up")
.loginProcessingUrl("/signInProcess").usernameParameter("phone").and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/")
.invalidateHttpSession(false).and().csrf().disable().cors();
}
}
}
by doing this JWT is working fine as just I need but the formlogin has stopped and calling "/signInProcess" now give 404:
NOTE: if I change the order and make formLogin #order(1) it works again but of course will not work.
Also I tried to combine them both like this now it is both works fine but the problem with exception handling if the JWT authentication error will return formlogin thymeleaf error page :
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/admins**").hasAnyRole("SADMIN").antMatchers("/admin/**")
.hasAnyRole("ADMIN", "SADMIN", "WADMIN").antMatchers("/rest/**")
.hasAnyRole("ADMIN", "SADMIN", "WADMIN", "USER").antMatchers("/user/**").hasAnyRole("USER")
.antMatchers("/api/user/authenticate").permitAll()
.antMatchers("/api/user/**").hasRole("USER")
.anyRequest().permitAll().and().formLogin().loginPage("/sign-in-up")
.loginProcessingUrl("/signInProcess").usernameParameter("phone").and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/")
.invalidateHttpSession(false).and().csrf().disable().cors();
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
any suggestions to make this work. thank you.
Your WebSecurityConfigurerAdapters will process the incoming requests in order.
Since JWTSecurityConfig is annotated with #Order(1) it will process the requests first.
You have not specified a antMatcher for this Adapter, so it will match all requests.
This means that a request will never reach FormLoginConfigurationAdapter, since JWTSecurityConfig matches them all.
If you want JWTSecurityConfig to only apply to certain requests, you can specify an antMatcher in your security configuration.
Below is an example:
#EnableWebSecurity
public class SecurityConfigurations {
#Configuration
#Order(1)
public class JWTSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers(matchers -> matchers
.antMatchers("/api/**") // apply JWTSecurityConfig to requests matching "/api/**"
)
.authorizeRequests(authz -> authz
.anyRequest().authenticated()
)
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
#Configuration
public class FormLoginConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authz -> authz
.anyRequest().authenticated()
)
.formLogin();
}
}
}
For more details on multiple WebSecurityConfigurerAdapter, you can see the multiple HttpSecurity section in the Spring Security reference docs.
For more details on the difference between authorizeRequests() and requestMatchers(), you can see this Stack Overflow question.
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));
}
}
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 am trying to configure SpringSecurity to work with Remember Me authentication.
Here is my Java configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Autowired
DatabasePersistentTokeRepositoryImpl databasePersistentTokeRepositoryImpl;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authenticationProvider(rememberMeAuthenticationProvider())
.rememberMe().tokenRepository(databasePersistentTokeRepositoryImpl).tokenValiditySeconds((int) TimeUnit.SECONDS.convert(7, TimeUnit.DAYS))
.and()
.csrf().disable();
}
#Bean()
public AuthenticationProvider rememberMeAuthenticationProvider() {
return new RememberMeAuthenticationProvider("KEY");
}
#Bean()
public TokenBasedRememberMeServices rememberMeServices() {
TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices("KEY", userDetailsService);
rememberMeServices.setAlwaysRemember(true);
return rememberMeServices;
}
}
I see that rememberMeServices is not injected in RememberMeConfigurer. And that results in creating RememberMeAuthenticationFilter which refers to wrong rememberMeServices.
There is a section in Spring Security documentation describing this process using XML.
http://docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#session-mgmt
What is wrong with my injection and is it possible do to this without XML after all?
You aren't injecting it. There is no autowiring for the RememberMeConfigurer. Also why are you configuring so many beans?
The RememberMeAuthenticationProvider is already created for you, if you want to use a different key specify it using key("KEY"). This in turn will be used to create the RememberMeServices.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Autowired
DatabasePersistentTokeRepositoryImpl databasePersistentTokeRepositoryImpl;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.rememberMe()
.key("KEY")
.tokenRepository(databasePersistentTokeRepositoryImpl)
.tokenValiditySeconds((int) TimeUnit.SECONDS.convert(7, TimeUnit.DAYS))
.and()
.csrf().disable();
}
}
If you really need to set the alwaysRemember property to true you could use an ObjectPostProcessor to post process the filter and configure the RememberMeServices from there.
You would also have injected the wrong type of RememberMeServices as the one configured doesn't use the PersistentTokeRepository.
Just to provide an example of the code that #m-deinum's suggestion of an ObjectPostProcessor that sets alwaysRemember to true would look like would look like:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.rememberMe()
.key("KEY")
.tokenRepository(databasePersistentTokeRepositoryImpl)
.tokenValiditySeconds((int) TimeUnit.SECONDS.convert(7, TimeUnit.DAYS))
.withObjectPostProcessor( new ObjectPostProcessor<RememberMeAuthenticationFilter>() {
#Override
public <O extends RememberMeAuthenticationFilter> O postProcess( O object) {
RememberMeAuthenticationFilter rmaf = (RememberMeAuthenticationFilter)
PersistentTokenBasedRememberMeServices rms = (PersistentTokenBasedRememberMeServices)rmaf.getRememberMeServices();
rms.setAlwaysRemember( true );
return object;
}
})
.and()
.csrf().disable();
}