带有OAuth2- ResourceServer的Spring微服务仅以String作为主体而不是我的自定义用户返回

时间:2018-06-25 18:14:46

标签: spring spring-boot oauth-2.0 spring-cloud spring-security-oauth2

我有两个微服务,一个配置为OAuth2服务器-A,另一个配置为OAuth2客户端-B。我想在这两个微服务之间共享我的自定义用户。当用户通过AI进行身份验证时,创建UserDetails的自定义实现,我想保护B中的某些资源。因此,我配置了与A相同的资源服务器。我希望可以在以下两者之间共享UserDetails的自定义实现A和B使用主体。我能够从A中的主体获取自定义用户,但在B中,主体仅由String(username)表示。如何使资源服务器返回UserDetails的自定义实现,而不仅仅是用户名?

source of A - server

服务器配置:

@SpringBootApplication
@EnableResourceServer
@EnableAuthorizationServer
@EnableWebSecurity
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

UserDetailsS​​ervice的实现,该实现返回我的实现UserDetails的CustomUser(为简单起见,只是一个虚拟用户)

@Service
@Primary
public class UserDetailsServiceImpl implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        GrantedAuthority authority = new Authority("USER");
        return new CustomUser(5L, "username", "{noop}password", Arrays.asList(authority));
    }
}

OAuth2服务器的配置:

@Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

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

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

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
               .withClient("user")
               .secret("{noop}password")
               .authorizedGrantTypes("password", "refresh_token")
               .scopes("webapp");
    }
}

WebSecurityConfigurerAdapter:

@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

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

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

通过剩余的端点(仅返回主体),这是主体“应该”可被其他服务访问的方式,在这里服务A中包含CustomUser的实例:

@RestController
@RequestMapping("/user")
public class UserRestController {

    @GetMapping("/auth")
    public Principal getPrincipal(Principal principal) {
        return principal;
    }
}

客户 source of B - client

Application.properties-来自A的终结点的URL

server.port=8081
security.oauth2.resource.user-info-uri=http://localhost:8080/user/auth

B的首发

@SpringBootApplication
@EnableResourceServer
@EnableWebSecurity
@EnableOAuth2Client
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

最后是我用来测试主体是字符串(用户名)还是CustomUser的rest控制器。不幸的是,主体始终只包含用户名和具有自定义用户值的字段映射。

@RestController
@RequestMapping("/client")
public class ClientRestController {

    @GetMapping("/print")
    public void printId(Principal principal) {
        System.out.println(principal);
    }
}

您能帮我解决吗?如何通过我的CustomUser传递Principal?

感谢帮助:)

1 个答案:

答案 0 :(得分:0)

好吧,返回Principal对象不是一个好主意(很可能它将是其实现类UsernamePasswordAuthenticationToken)。如果只希望端点共享自定义用户详细信息,那么最好的方法是创建一个仅包含您要公开的信息的数据传输对象。

建议的获取主体(提出请求的经过身份验证的用户)的方法是来自安全上下文。有关更多信息,请访问here