I would like to make some urls of my api public. But once I configure one single url, all my api become exposed without authorization.
Below my configure method of ResourceServerConfiguration class :
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/books","/api/plainOffers","/api/offers","/api/public/*").permitAll();
}
ResourceServer configuration :
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/api/books").permitAll();
http.authorizeRequests().antMatchers("/api/plainOffers").permitAll();
http.authorizeRequests().antMatchers("/api/offers").permitAll();
http.authorizeRequests().antMatchers("/api/public/*").permitAll();
//http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}
Authorization server :
#CrossOrigin
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig
extends AuthorizationServerConfigurerAdapter{
#Autowired
#Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Value("${api.oauth.tokenTimeout:3600}")
private int expiration;
#Override
public void configure(AuthorizationServerEndpointsConfigurer configurer) throws Exception {
configurer.authenticationManager(authenticationManager);
configurer.userDetailsService(userDetailsService);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("api")
.secret("secret")
.accessTokenValiditySeconds(expiration)
.scopes("read", "write")
.authorizedGrantTypes("password", "refresh_token")
.resourceIds("oauth2-resource");
}
}
I guess you'll have to add the required restricions on other urls :
http.authorizeRequests().antMatchers("/api/books","/api/plainOffers","/api/offers","/api/public/*").permitAll();
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
I would like to point out that if you have provided resource_id = api then remove /api and add urls like this ("/books","/plainOffers","/offers","/public/*") in rules.
Then it will work.
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 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.
I have Spring web app with REST WS built using spring-security-oauth2:2.3.3 and spring-boot-starter-security:2.0.2. But I am not able to set which endpoints are protected.
ResourceServerConfig
#EnableResourceServer
#Configuration
public class ResourceServerConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/customers", "/v2/**").permitAll()
.antMatchers("/api/**").authenticated();
}
#Bean
public AuthenticationManager authenticationManager(AuthenticationProvider authenticationProvider) {
return new CustomAuthenticationManager(authenticationProvider);
}
#Bean
public AuthenticationProvider authenticationProvider(UserRepository userRepository, PasswordEncoder passwordEncoder) {
return new DBAuthenticationProvider(userRepository, passwordEncoder);
}
}
AuthorizationServerConfig
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("ClientId")
.secret("secret")
.authorizedGrantTypes("password")
.scopes("read", "write");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(authenticationManager);
}
}
When I obtain access token first, everything works perfectly, but I expect endpoints "/", "/customers", "/v2/**" to be permited for all without any authorization. But when I call 'curl http://{{host}}/' or http://{{host}}/customers I still get 401:
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
adding "--header "Authorization: Basic {{base64 client id:password}}" doesnt help either. How is that possible?
EDIT
according to spring security permitAll still considering token passed in Authorization header and returns 401 if token is invalid solved by adding #Order(1) to ResourceServerConfig to override default OAuth2. But that should kick in only when "Authorization" header is added, which is not my case. So how is this possible?
Add this to your project.
#Configuration
public class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/user/**")
.authorizeRequests().anyRequest().authenticated();
}
}
If you want to make any request to /user/** you need to pass your access token in the header as Authorization: Bearer {access_token}. All other endpoints will not ask for a token.
Im trying to split the resource server from the authorization server in spring-boot. I have two different applications that i'm running separately. In the authorization server i can get the bearer token from oauth/token but when i'm trying to get access to the resource(sending the token in header) i'm getting an invalid token error. My intention is to use the InMemoryTokenStore and the bearer token. Can anyone tell me what is wrong in my code?
Authorization Server:
#SpringBootApplication
public class AuthorizationServer extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(AuthorizationServer.class, args);
}
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
private TokenStore tokenStore = new InMemoryTokenStore();
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.tokenStore(tokenStore);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("hasAuthority('ROLE_USER')");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("user")
.secret("password")
.authorities("ROLE_USER")
.authorizedGrantTypes("password")
.scopes("read", "write")
.accessTokenValiditySeconds(1800);
}
}
Resource Server:
#SpringBootApplication
#RestController
#EnableOAuth2Resource
#EnableWebSecurity
#Configuration
public class ResourceServer extends WebSecurityConfigurerAdapter {
public static void main(String[] args){
SpringApplication.run(ResourceServer.class, args);
}
#RequestMapping("/")
public String home(){
return "Hello Resource World!";
}
#Bean
public ResourceServerTokenServices tokenService() {
RemoteTokenServices tokenServices = new RemoteTokenServices();
tokenServices.setClientId("user");
tokenServices.setClientSecret("password");
tokenServices.setTokenName("tokenName");
tokenServices.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
return tokenServices;
}
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
OAuth2AuthenticationManager authenticationManager = new OAuth2AuthenticationManager();
authenticationManager.setTokenServices(tokenService());
return authenticationManager;
}
#Configuration
#EnableResourceServer
protected static class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/","/home")
.and()
.authorizeRequests()
.anyRequest().access("#oauth2.hasScope('read')");
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
TokenStore tokenStore = new InMemoryTokenStore();
resources.resourceId("Resource Server");
resources.tokenStore(tokenStore);
}
}
You have created 2 instances of InMemoryTokenStore. If you want to share tokens between the auth server and resource server they need the same store.
I'm following the basic Spring Boot OAuth2 example from Dave Syer: https://github.com/dsyer/sparklr-boot/blob/master/src/main/java/demo/Application.java
#Configuration
#ComponentScan
#EnableAutoConfiguration
#RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#RequestMapping("/")
public String home() {
return "Hello World";
}
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
// Just for laughs, apply OAuth protection to only 2 resources
.requestMatchers().antMatchers("/","/admin/beans").and()
.authorizeRequests()
.anyRequest().access("#oauth2.hasScope('read')");
// #formatter:on
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("sparklr");
}
}
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients.inMemory()
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.resourceIds("sparklr")
.accessTokenValiditySeconds(60)
.and()
.withClient("my-client-with-registered-redirect")
.authorizedGrantTypes("authorization_code")
.authorities("ROLE_CLIENT")
.scopes("read", "trust")
.resourceIds("sparklr")
.redirectUris("http://anywhere?key=value")
.and()
.withClient("my-client-with-secret")
.authorizedGrantTypes("client_credentials", "password")
.authorities("ROLE_CLIENT")
.scopes("read")
.resourceIds("sparklr")
.secret("secret");
// #formatter:on
}
}
}
The example works very well for both types of grants, but the password grant uses the Spring Boot default security user (the one that echo's out "Using default security password: 927ca0a0-634a-4671-bd1c-1323a866618a" during startup).
My question is how do you override the default user account and actually rely on a WebSecurityConfig? I've added a section like this:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder)
throws Exception {
authManagerBuilder.inMemoryAuthentication().withUser("user")
.password("password").roles("USER");
}
}
But it does not seem to override the default Spring user/password even though the documentation suggests that it should.
What am I missing to get this working?
As I'm still on 2.0.3, I tried a few more things and this appears to be working:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder
.inMemoryAuthentication()
.withUser("user1").password("password1").roles("USER").and()
.withUser("admin1").password("password1").roles("ADMIN");
}
#Bean
#Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}
By explicitly defining the authenticationManager bean, the built-in user authentication went away and it started relying on my own inMemoryAuthentication. When 2.0.4 is released, I'll re-evaluate the solution that Dave posted above as it looks like it will be more elegant.
#Configuration
protected static class AuthenticationManagerConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("min").password("min").roles("USER");
}
}