OAuth2-SpringBoot - 刷新令牌

时间:2017-07-09 20:28:54

标签: java spring-boot oauth-2.0

我已将Spring启动应用程序配置为提供oauth2授权。

        @Configuration
    public class OAuth2Configuration {

        @Configuration
        @EnableResourceServer
        protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

            @Autowired
            private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

            @Autowired
            private CustomLogoutSuccessHandler customLogoutSuccessHandler;

            @Override
            public void configure(HttpSecurity http) throws Exception {

                http
                        .exceptionHandling()
                        .authenticationEntryPoint(customAuthenticationEntryPoint)
                        .and()
                        .logout()
                        .logoutUrl("/oauth/logout")
                        .logoutSuccessHandler(customLogoutSuccessHandler)
                        .and()
                        .csrf()
                        .disable()
                        .headers()
                        .frameOptions().disable()
                        .exceptionHandling().and()
                        .sessionManagement()
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                        .and()
                        .authorizeRequests()
                        .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                        .antMatchers("/api/v1/login/**").permitAll()
                        .antMatchers("/api/v1/admin/**").permitAll()
                        .antMatchers("/api/v1/test/**").permitAll()
                        .antMatchers("/oauth/token").permitAll()
                        .antMatchers("/api/**").authenticated();
            }

        }

        @Configuration
        @EnableAuthorizationServer
        protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {

            private static final String ENV_OAUTH = "authentication.oauth.";
            private static final String PROP_CLIENTID = "clientid";
            private static final String PROP_SECRET = "secret";
            private static final String PROP_TOKEN_VALIDITY_SECONDS = "tokenValidityInSeconds";

            private RelaxedPropertyResolver propertyResolver;

            @Autowired
            private DataSource dataSource;

            @Bean
            public TokenStore tokenStore() {
                return new JdbcTokenStore(dataSource);
            }

            @Autowired
            @Qualifier("authenticationManagerBean")
            private AuthenticationManager authenticationManager;

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

            @Override
            public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
                clients
                        .inMemory()
                        .withClient(propertyResolver.getProperty(PROP_CLIENTID))
                        .scopes("read", "write")
                        .authorities(Authorities.ROLE_USER.name())
                        .authorizedGrantTypes("password", "refresh_token", "authorization_code", "implicit")
                        .secret(propertyResolver.getProperty(PROP_SECRET))
                        .accessTokenValiditySeconds(
                                propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 1800))
                        .refreshTokenValiditySeconds(100000);
            }

            @Override
            public void setEnvironment(Environment environment) {
                this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
            }

        }

    }


        @Configuration
    @EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

        @Autowired
        private UserDetailsService userDetailsService;

        @Bean
        public CustomPasswordEncoder passwordEncoder() {
            return new CustomPasswordEncoder();
        }

        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

            auth
                    .userDetailsService(userDetailsService)
                    .passwordEncoder(passwordEncoder());

        }

        @Override
        public void configure(WebSecurity web) throws Exception {

            web
                    .ignoring()
                    .antMatchers(HttpMethod.OPTIONS, "/**").antMatchers("/api/login/**");
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.httpBasic().realmName("WebServices").and().sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().requestMatchers()
                    .antMatchers("/oauth/authorize").and().authorizeRequests().antMatchers("/oauth/authorize")
                    .authenticated();
        }

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

        @EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
        private static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
            @Override
            protected MethodSecurityExpressionHandler createExpressionHandler() {
                return new OAuth2MethodSecurityExpressionHandler();
            }

        }

    }


    public class UserDetailsServiceImpl implements UserDetailsService {

    @Inject
    private AccountDao accountDao;

    @Override
    @Transactional
    public UserDetails loadUserByUsername(final String login) {
        Account userFromDatabase = null;
        String lowercaseLogin = login.toLowerCase();
        if (lowercaseLogin.contains("@")) {
            userFromDatabase = accountDao.getByEmailId(lowercaseLogin);
        } else {
            userFromDatabase = accountDao.getByPhoneNumber(lowercaseLogin);
        }

        if (userFromDatabase != null) {
            if (!userFromDatabase.getActivated()) {
                throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated");
            }
            List<GrantedAuthority> grantedAuthorities = userFromDatabase.getRoles().stream()
                    .map(authority -> new SimpleGrantedAuthority(authority.getRoleName())).collect(Collectors.toList());
            return new org.springframework.security.core.userdetails.User(userFromDatabase.getAccountName(),
                    userFromDatabase.getAccountPassword(), grantedAuthorities);
        } else {
            throw new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the " + "database");
        }
    }

}

现在每当我尝试在访问令牌到期后获取刷新令牌时,我总是得到

  

2017-07-10 00:57:40.797 INFO 68115 --- [nio-9090-exec-4]   o.s.s.o.provider.endpoint.TokenEndpoint:处理错误:   NoSuchClientException,没有请求id的客户端:12345678

虽然数据库中有一行,列号为12345678,帐号为12345678。

https://myTestWebServices/oauth/token?grant_type=refresh_token&refresh_token=f4cc8213-3f2b-4a30-965b-6feca898479e

我将标头设置为Authorization:Basic xxx xxx与我用来获取access_token的相同,所以我假设它工作正常。

但输出总是这个

  

{&#34;错误&#34;:&#34;未经授权&#34;,&#34; error_description&#34;:&#34;用户12345678原为   没有在数据库中找到&#34; }

3 个答案:

答案 0 :(得分:2)

我认为对于密码grant_type,需要clientId和clientSecret。您在Authorization标头中传递Base64编码的clientId和clientSecret而不是访问令牌。像这样:

curl -H "Authorization: Bearer [base64encode(clientId:clientSecret)]" "https://yourdomain.com/oauth/token?grant_type=refresh_token&refresh_token=[yourRefreshToken]"

我假设您第一次得到这样的令牌(即使我问过你也没说过):

curl --data "grant_type=password&username=user&password=pass&client_id=my_client" http://localhost:8080/oauth/token"

另外,在loadUserByUsername中放置一个断点,并检查是否为失败的刷新尝试调用了它。

答案 1 :(得分:0)

您应在获取时传递 clientId client secret (与 userId password 不同) access token使用refresh token。不确定authorisation标头中传递了什么。

您似乎遇到了两个不同的问题。什么时候出现以下错误:

  

{“ error”:“未经授权”,“ error_description”:“用户12345678不是   在数据库中找到”“}

您可以验证用户是否已成功身份验证以及服务是否返回了访问令牌和刷新令牌?您可以将调试指针放在UserDetailsService中,然后检查流。

尝试通过以下步骤验证配置:

获取刷新令牌(假设您正在使用

curl -vu clientId:clientSecret 'http://your_domain_url/api/oauth/token?username=userName&password=password&grant_type=password'

此处用户名密码客户端ID 客户端密码 不同 这应该返回您刷新令牌并在响应中访问令牌

{"access_token":"d5deb98a-75fc-4f3a-bbfd-e5c87ca2ca6f","token_type":"bearer","refresh_token":"b2be4291-57e9-4b28-b114-feb3406e030d","expires_in":2,"scope":"read write"}

以上响应已获得访问令牌和刷新令牌。只要访问令牌过期,您就可以使用刷新令牌来获取访问令牌,如下所示:

curl -vu clientId:clientSecret 'http://your_domain_url/api/oauth/token?grant_type=refresh_token&refresh_token=refresh_token_value'

响应:

{"access_token":"13fd30f9-f0c5-414e-9fbd-a5e2f9f3e4a7","token_type":"bearer","refresh_token":"b2be4291-57e9-4b28-b114-feb3406e030d","expires_in":2,"scope":"read write"}

现在您可以使用访问令牌拨打服务电话

curl -i -H "Authorization: Bearer 13fd30f9-f0c5-414e-9fbd-a5e2f9f3e4a7" http://your_domain_url/api/mySecureApi

答案 2 :(得分:0)

只需添加UserDetailsS​​ervice即可使用

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

并请求

http://localhost:8080/oauth/token?grant_type=refresh_token&client_id=myclient&client_secret=secret&refresh_token=<token>