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
Related
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 am trying to secure REST api in my Spring Boot application by oAuth2 standard. I created classes extending AuthorizationServerConfigurerAdapter, ResourceServerConfigurerAdapter and WebSecurityConfigurerAdapter and unfortunately it works only with login scenario. /oauth/token request returns access_token etc, but in every other case I get 401, Unauthorized, Bad credentials. I thought that wrong cliendId/secret was the case, but it would also affect login, right?
P.S Application is connected to MySql database.
I already ran out of ideas, so maybe you guys are able to help me.
WebSecurityConfig.java :
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/oauth/token").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
AuthorizationServerConfig.java :
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client")
.authorizedGrantTypes("client-credentials", "password","refresh_token")
.authorities("ROLE_CLIENT", "ROLE_ANDROID_CLIENT")
.scopes("read", "write", "trust")
.resourceIds("oauth2-resource")
.accessTokenValiditySeconds(5000)
.secret("secret")
.refreshTokenValiditySeconds(50000);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
}
ResourceServerConfig.java :
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/api/**").authenticated();
}
}
MainApplication.java :
#SpringBootApplication
#ComponentScan({"com.user.app"})
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
#Autowired
public void authenticationManaget(AuthenticationManagerBuilder builder, UserRepository repository) throws Exception {
builder.userDetailsService(new UserDetailsServiceImpl());
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
Ok, somehow oAuth2 configuration was overwritten by base configuration in some places. Those places still weren't protected by oAuth, so that's why "Bad Credentials" error was occurring even after providing proper access token. Commenting configure() method inside WebSecurityConfig solved the problem.
How can I define a #RequestMapping method to explicit allow anonymous (unauthorized) access?
The following does not work, always getting 401 Unauthorized:
#RequestMapping("/test")
#Secured(value={"ROLE_ANONYMOUS"})
public String test() {
return "OK";
}
In general the whole application is secured as follows, using spring-boot:
security.basic.enabled=true.
#Configuration
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
}
You can override configure(HttpSecurity httpSecurity) method and define your rules there:
#Configuration
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
httpSecurity.authorizeRequests()
.antMatchers("/test")
.permitAll();
super.configure(http);
}
}
#Configuration
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
httpSecurity.authorizeRequests().regexMatcher("^((?!test).)*$").permitAll();
}
}
I am not sure about a slash before test but just try this negative look-around method.
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();
}
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();
}
}