如何在Spring-SECURITY-OAuth2中获取资源服务器中的自定义UserDetailService对象? [英] How to get custom UserDetailService Object in Resource Server in spring-security-oauth2?

查看:9
本文介绍了如何在Spring-SECURITY-OAuth2中获取资源服务器中的自定义UserDetailService对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有单独的授权服务器和资源服务器。 授权服务器指向单独的数据库。我已经使用CustomUserDetailService获取用户相关信息。 我已使用CustomTokenEnhancer获取除响应中的令牌之外的其他信息。

@Configuration
public class OAuth2Configuration {


    @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;

        @Autowired
        private CustomUserDetailService userDetailsService;

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

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

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints)
                throws Exception {
            endpoints
                    .tokenStore(tokenStore())
                    .userDetailsService(userDetailsService)

                    .tokenEnhancer(tokenEnhancer())
                    .accessTokenConverter(accessTokenConverter())

                    .authenticationManager(authenticationManager);
        }

        @Bean
        public TokenEnhancer tokenEnhancer() {
           return new CustomTokenEnhancer();
        }

        @Bean
        public DefaultAccessTokenConverter accessTokenConverter() {
            return new DefaultAccessTokenConverter();
        }


        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer)
                throws Exception {
            oauthServer
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()");
        }
    }

}

CustomUserDetailService类:

@Service
public class CustomUserDetailService implements UserDetailsService {


    @Autowired
    private AccountRepository accountRepository;

    @Override
    public UserDetails loadUserByUsername(String username) {

        Account account = accountRepository.getByEmail(username);

        if(account == null) {
            throw new UsernameNotFoundException(username);
        }

        return new MyUserPrincipal(account);

    }
}

CustomTokenEnhancer类:

public class CustomTokenEnhancer implements TokenEnhancer {

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        MyUserPrincipal user = (MyUserPrincipal) authentication.getPrincipal();
        final Map<String, Object> additionalInfo = new HashMap<>();

        additionalInfo.put("user_information", user.getAccount());

        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);

        return accessToken;
    }

}

请求/响应

http://localhost:9191/authserver/oauth/token


{
    "access_token": "fddb571e-224e-4cd7-974e-65104dd24b41",
    "token_type": "bearer",
    "refresh_token": "eb412b00-9e4e-4d6c-86d8-324d999b5f08",
    "expires_in": 100,
    "scope": "read write",
    "account_information": {
        "id": 14,
        "firstname": "name",
        "lastname": "lastname",

    }
}

在资源服务器端,我已经使用RemoteTokenSerice验证了用户提交的令牌是否有效。

@Configuration
@EnableResourceServer
public class OAuthResourceConfig extends ResourceServerConfigurerAdapter {

    private TokenExtractor tokenExtractor = new BearerTokenExtractor();

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.addFilterAfter(new OncePerRequestFilter() {
            @Override
            protected void doFilterInternal(HttpServletRequest request,
                    HttpServletResponse response, FilterChain filterChain)
                    throws ServletException, IOException {
                if (tokenExtractor.extract(request) == null) {
                    SecurityContextHolder.clearContext();
                }
                filterChain.doFilter(request, response);
            }
        }, AbstractPreAuthenticatedProcessingFilter.class);
        http.csrf().disable();
        http.authorizeRequests().anyRequest().authenticated();
    }

    @Bean
    public AccessTokenConverter accessTokenConverter() {
        return new DefaultAccessTokenConverter();
    }

    @Bean
    @Primary
    public RemoteTokenServices remoteTokenServices(final @Value("${auth.server.url}") String checkTokenUrl,
            final @Value("${auth.server.clientId}") String clientId,
            final @Value("${auth.server.clientsecret}") String clientSecret) {


        final RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
        remoteTokenServices.setCheckTokenEndpointUrl(checkTokenUrl+"?name=value");
        remoteTokenServices.setClientId(clientId);
        remoteTokenServices.setClientSecret(clientSecret);
        remoteTokenServices.setAccessTokenConverter(accessTokenConverter());
        return remoteTokenServices;
    }

    }
因此它工作正常,当我使用令牌向资源服务器发出请求时,如果令牌有效,它就会处理该请求。我的问题是,我想获取资源服务器中的Account对象。我试了一下:

 Account account = (Account)SecurityContextHolder.getContext().getAuthentication().getPrincipal()

但它给出的是字符串而不是完整的用户定义对象,因此引发异常。如何在资源服务器的任何控制器中获取Account对象?

{
    "timestamp": 1499334657703,
    "status": 500,
    "error": "Internal Server Error",
    "exception": "java.lang.ClassCastException",
    "message": "java.lang.String cannot be cast to Account",
    "path": "/secure"
    }

我尝试了link,但是否可以同时注入两个令牌服务:RemoteTokenService和CustomUserInfoTokenServices?

我还认为Spring在这里从资源服务器到授权服务器(http://localhost:9191/authserver/oauth/check_token?token=d8dae984-7bd8-4aab-9990-a2c916dfe667)进行内部调用以验证令牌。

是否可以在不再次调用此终结点的情况下在控制器中获取这些信息。

响应:

{
    "exp": 1499333294,
    "account_information": {
        "accountid": 14,
        "firstname": "fname",
        "lastname": "lname",

    },
    "user_name": "abc@abc.com",
    "client_id": "clientId",
    "scope": [
        "read",
        "write"
    ]
}

推荐答案

我已重写了Below方法并添加了一些逻辑。

public class CustomAccessTokenConverter extends DefaultAccessTokenConverter{

    private UserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter();

    @Override
    public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
        Map<String, String> parameters = new HashMap<String, String>();
        @SuppressWarnings("unchecked")
        Set<String> scope = new LinkedHashSet<String>(map.containsKey(SCOPE) ? (Collection<String>) map.get(SCOPE)
                : Collections.<String>emptySet());
        Authentication user = userTokenConverter.extractAuthentication(map);
        String clientId = (String) map.get(CLIENT_ID);
        parameters.put(CLIENT_ID, clientId);
        parameters.put("account_information", String.valueOf((((Map) map.get("account_information")).get("accountid"))));
        @SuppressWarnings("unchecked")
        Set<String> resourceIds = new LinkedHashSet<String>(map.containsKey(AUD) ? (Collection<String>) map.get(AUD)
                : Collections.<String>emptySet());

        Map<String, Serializable> extensions = new HashMap<String, Serializable>();
        extensions.put("account_information", (HashMap) map.get("account_information"));

        OAuth2Request request = new OAuth2Request(parameters, clientId, null, true, scope, resourceIds, null, null,
                extensions);
        return new OAuth2Authentication(request, user);
    }

}

资源服务器类

@Bean
    public AccessTokenConverter accessTokenConverter() {
        //return new DefaultAccessTokenConverter();
        return new CustomAccessTokenConverter();
    }

    @Bean
    @Primary
    public RemoteTokenServices remoteTokenServices(final @Value("${auth.server.url}") String checkTokenUrl,
            final @Value("${auth.server.clientId}") String clientId,
            final @Value("${auth.server.clientsecret}") String clientSecret) {


        final RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
        remoteTokenServices.setCheckTokenEndpointUrl(checkTokenUrl+"?name=value");
        remoteTokenServices.setClientId(clientId);
        remoteTokenServices.setClientSecret(clientSecret);
        remoteTokenServices.setAccessTokenConverter(accessTokenConverter());
        return remoteTokenServices;
    }

现在我可以在控制器中获取其他信息。

OAuth2Authentication authentication = (OAuth2Authentication)SecurityContextHolder.getContext().getAuthentication();

        Map<String, Serializable> map = authentication.getOAuth2Request().getExtensions();

这篇关于如何在Spring-SECURITY-OAuth2中获取资源服务器中的自定义UserDetailService对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆