I am trying to implement a authentication-server using spring boot, spring cloud security and spring cloud oauth2.
I am getting below error when I try to hit http://localhost:8080/auth/oauth/token from postman
{ "error": "unauthorized", "error_description": "Full authentication is required to access this resource" }
Below is my pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> . <modelVersion>4.0.0</modelVersion> <groupId>com.teckink.tp</groupId> <artifactId>tp-auth-server</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>tp-auth-server</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.M9</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project>
Starter(Main) class:
package com.teckink.tp.authserver; import java.util.HashMap; import java.util.Map; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @RestController @EnableResourceServer @EnableAuthorizationServer public class App { @RequestMapping(value = { "/user" }, produces = "application/json") public Map<String, Object> user(OAuth2Authentication user) { Map<String, Object> userInfo = new HashMap<>(); userInfo.put("user", user.getUserAuthentication().getPrincipal()); userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities())); return userInfo; } public static void main(String[] args) { SpringApplication.run(App.class, args); } }
OAuth2Config class which defines client and its secret:
package com.teckink.tp.authserver.security; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; @Configuration public class OAuth2Config extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailsService userDetailsService; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("eagleeye") .secret("thisissecret") .authorizedGrantTypes("refresh_token", "password", "client_credentials") .scopes("webclient", "mobileclient"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); } }
WebSecurityConfigurer class which defines in-memory user, password and roles:
package com.teckink.tp.authserver.security; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; @Configuration public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter { @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override @Bean public UserDetailsService userDetailsServiceBean() throws Exception { return super.userDetailsServiceBean(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("john.carnell").password("password1").roles("USER") .and() .withUser("william.woodward").password("password2").roles("USER", "ADMIN"); } }
I am calling Rest API from POSTMAN as below- Request with authentication screen show as below :
2 Answers
Answers 1
In your sample app when you run, you would have got the following exception
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
In spring-security-core:5.0, the default PasswordEncoder is built as a DelegatingPasswordEncoder. Hence when you store the users in memory, you are providing the passwords in plain text and then when you are trying to retrieve the encoder from the DelegatingPasswordEncoder to validate the password it can't find one.
More details in this link Password Encoding
To address this , for a production implementation you should activate an instance of BCryptPasswordEncoder
For development, you can try out the following changes so that you can override password encoding by adding {noop}
to the password value. This will treat the password by activating the NoOpPasswordEncoder
instead of the default DelegatingPasswordEncoder
and will treat your password as plain text.
OAuth2Config class
clients.inMemory() .withClient("eagleeye") .secret("{noop}thisissecret") .authorizedGrantTypes("refresh_token", "password", "client_credentials") .scopes("webclient", "mobileclient");
WebSecurityConfigurer class
auth .inMemoryAuthentication() .withUser("john.carnell"). password("{noop}password1").roles("USER") .and() .withUser("william.woodward").password("{noop}password2").roles("USER", "ADMIN");
Now when you try from Postman, you would be able to generate token
EDIT
Github project with a working demo here
Answers 2
I noticed that your grant type is listed as "passwor" and not "password" in your post Can you please correct that and try once more?
0 comments:
Post a Comment