【问题标题】:OAuth 2: Problems to get access tokenOAuth 2:获取访问令牌的问题
【发布时间】:2016-07-18 15:40:44
【问题描述】:

我刚刚开始使用 OAuth 和 Spring Boot,并做了一个基本的应用程序来查看它的实际效果。

这是我的 MySQL 架构:

CREATE TABLE `role` (
  `id` int(20) NOT NULL,
  `role` varchar(50) NOT NULL,
  PRIMARY KEY (`role`)
)

CREATE TABLE `user` (
  `name` varchar(255) DEFAULT NULL,
  `username` varchar(50) NOT NULL,
  `email` varchar(50) DEFAULT NULL,
  `password` varchar(500) DEFAULT NULL,
  `enabled` tinyint(1) DEFAULT '0',
  `activationkey` varchar(50) DEFAULT NULL,
  `resetpasswordkey` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`username`),
  KEY `name` (`name`)
)

CREATE TABLE `user_role` (
  `username` varchar(50) NOT NULL,
  `role` varchar(50) NOT NULL,
  `id` int(20) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `USER_FK` (`username`),
  KEY `ROLE_FK` (`role`),
  CONSTRAINT `ROLE_FK` FOREIGN KEY (`role`) REFERENCES `role` (`role`),
  CONSTRAINT `USER_FK` FOREIGN KEY (`username`) REFERENCES `user` (`name`)
)

还有数据:

INSERT INTO `role` VALUES ('1', 'ROLE_ADMIN');
INSERT INTO `role` VALUES ('3', 'ROLE_GUEST');
INSERT INTO `role` VALUES ('2', 'ROLE_USER');

INSERT INTO `user` VALUES ('Leonardo', 'leonardo', 'leonardo.mora@datys.cu', 'admin', '1', null, null);

INSERT INTO `user_role` VALUES ('leonardo', 'ROLE_ADMIN', '0');

还有代码:

/**
* My CustomUserDetailService
*/
@Service
public class CustomUserDetailService implements UserDetailsService {

    private final UserRepository userRepository;

    @Autowired
    public CustomUserDetailService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException(String.format("User %s does not exist!", username));
        }
        return new UserRepositoryUserDetails(user);
    }

    private static class UserRepositoryUserDetails extends User implements UserDetails {

        private UserRepositoryUserDetails(User user) {
            super(user);
        }

        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return getRoles();
        }

        @Override
        public boolean isAccountNonExpired() {
            return true;
        }

        @Override
        public boolean isAccountNonLocked() {
            return true;
        }

        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }

        @Override
        public boolean isEnabled() {
            return getEnabled();
        }
    }
}

OAuth 配置:

@Configuration
public class Oauth2JdbcSample extends WebSecurityConfigurerAdapter {

    private static final String RESOURCE_ID = "cenergy";

    /**
     * Esta clase es para configurar el servidor de recursos
     *
     * @author leonardo.mora
     */
    @Configuration
    @EnableResourceServer
    protected static class ResourceServer extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(ResourceServerSecurityConfigurer resources)
                throws Exception {
            resources
                    .resourceId(RESOURCE_ID);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()                    
                    .antMatchers("/users")
                    .authenticated();
        }
    }

    /**
     * Esta clase es para configurar el servidor de autorizacion
     *
     * @author leonardo.mora
     */
    @Configuration
    @EnableAuthorizationServer
    protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {

        private TokenStore tokenStore = new InMemoryTokenStore();

        @Autowired        
        private AuthenticationManager authenticationManager;

        @Autowired
        private CustomUserDetailService userDetailsService;

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints)
                throws Exception {
            endpoints.tokenStore(this.tokenStore)
                    .authenticationManager(this.authenticationManager)
                    .userDetailsService(this.userDetailsService);
        }

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            // @formatter:off          
            clients.inMemory()
                    .withClient("cenergy-client")
                    .authorizedGrantTypes("password")
                    .authorities("ROLE_ADMIN")
                    .scopes("read", "write")
                    .resourceIds(RESOURCE_ID)
                    .secret("123456")
                    .accessTokenValiditySeconds(300);
            // @formatter:on
        }

        @Bean
        @Primary
        public DefaultTokenServices tokenServices() {
            DefaultTokenServices tokenServices = new DefaultTokenServices();
            tokenServices.setSupportRefreshToken(true);
            tokenServices.setTokenStore(this.tokenStore);
            return tokenServices;
        }
    }
}

网络安全配置:

public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserDetailService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().csrfTokenRepository(csrfTokenRepository());
    }

    private CsrfTokenRepository csrfTokenRepository() {
        HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
        repository.setSessionAttributeName("_csrf");
        return repository;
    }
}

这是我的访问令牌请求:

curl -v -H "Content-Type: application/json" \
cenergy-client:123456@localhost:9191/api/oauth/token \
-d grant_type=password -d username=leonardo -d password=admin

这给了我错误:

{"error":"invalid_request","error_description":"Missing grant type"}

怎么了?

【问题讨论】:

    标签: spring curl oauth spring-boot


    【解决方案1】:

    对令牌端点的请求需要将其正文格式化为application/x-www-form-urlencoded 而不是application/json

    curl -v -H "Content-Type: application/x-www-form-urlencoded"  \
      cenergy-client:123456@localhost:9191/api/oauth/token        \
      -d grant_type=password -d username=leonardo -d password=admin
    

    这是在 OAuth2 规范中定义的(对于密码授权,专门在 Section 4.3. 中):

    4.3.2。访问令牌请求

    客户端使用 "application/x-www-form-urlencoded" 格式每 Appendix B 与 HTTP请求实体体中UTF-8的字符编码:

    【讨论】:

    • 谢谢 sthzg,我确实尝试过你的解决方案,用 curl -v -H "Content-Type: application/x-www-form-urlencoded" localhost:9191/api/oauth/token 更改我的 curl -d grant_type=password -d username=leonardo -d password=admin -d client_id=cenergy-client 结果是 {"error":"unauthorized","error_description":"访问此资源需要完全身份验证"} * 到主机 localhost 的连接 #0 保持不变
    • @LeonardoMoraLópez 如果您在 Curl 命令中将 client_id:password 放在主机地址之前,它应该可以工作(请参阅答案中的示例),例如cenergy-client:123456@localhost:9191/api/oauth/token。还从 Curl 中删除 -d client_id=...,因为令牌端点不支持该参数。
    • 这是我的 curl: curl -v -H "Content-Type: application/x-www-form-urlencoded" cenergy-client:123456@loc​​alhost:9191/api/oauth/token -d grant_type=password -d username=leonardo -d password=admin 现在我得到了错误的凭据,看看结果: {"error":"invalid_grant","error_description":"Bad credentials"}* Closing connection 0
    猜你喜欢
    • 2018-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-22
    • 2013-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多