如何在请求授权码之前对我的用户进行身份验证?是的,我为什么要这样做



发送以下请求时:

curl -i -H "Accept:application/json" -H "Content-Type: application/json" "http://localhost:8080/api/auth/authorize?client_id=ng-zero&redirect_uri=http%3A%2F%2Flocalhost%3A4200%2Fcallback&response_type=code&scope=read_profile" -X POST -d "{ "username" : "xxxx@yahoo.se", "password" : "xxxxxx" }"

我收到错误消息:InsufficientAuthenticationException: User must be authenticated with Spring Security before authorization can be completed

我正在尝试使用Spring Boot 2.0.4.RELEASE实现OAuth2提供程序,spring-security-oauth2-autoconfigure 2.0.6.RELEASE具有authorization codeOAuth2授权类型。

我知道/oauth/authorize端点是一个安全的端点,在向它发送请求之前,用户必须经过身份验证

我的授权服务器应该有login控制器还是login过滤器?

顺便说一句,当我的前端是Angular时,我想知道是否应该使用authorization code授权类型。。可能是password授权类型更合适吗?

但是,在上述请求中,如何将产生的"是的,用户已成功登录"状态携带到授权服务器?

我显式地打开了/auth/login端点,显式地保护了/auth/token端点,并且我没有说任何关于/auth/authorize端点的内容。

注意,我重新映射了两个端点:

/oauth/authorize -> /auth/authorize
/oauth/token     -> /auth/token

我有以下授权服务器的安全配置:

@EnableWebSecurity
@ComponentScan(nameGenerator = PackageBeanNameGenerator.class, basePackages = { "xxx.xxxxxxxxx.user.rest.service", "xxx.xxxxxxxxx.user.rest.filter" })
public class SecurityAuthorizationServerConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Autowired
private RESTAuthenticationEntryPoint restAuthenticationEntryPoint;
@Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable();
http.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.authorizeRequests()
.antMatchers(getUnsecuredPaths().toArray(new String[]{})).permitAll()
.antMatchers(RESTConstants.SLASH + DomainConstants.AUTH + RESTConstants.SLASH + DomainConstants.TOKEN).authenticated()
}
private List<String> getUnsecuredPaths() {
List<String> unsecuredPaths = Arrays.asList(
RESTConstants.SLASH + DomainConstants.AUTH + RESTConstants.SLASH + DomainConstants.LOGIN
);
return unsecuredPaths;
}
}

我的授权服务器配置为:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
static final String CLIENT_ID = "ng-zero";
static final String CLIENT_SECRET = "secret";
static final String GRANT_TYPE_PASSWORD = "password";
static final String GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code";
static final String GRANT_TYPE_REFRESH_TOKEN = "refresh_token";
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtProperties jwtProperties;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private TokenAuthenticationService tokenAuthenticationService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(CLIENT_ID)
.secret(CLIENT_SECRET)
.redirectUris("http://localhost:4200/callback")
.authorizedGrantTypes(GRANT_TYPE_PASSWORD, GRANT_TYPE_AUTHORIZATION_CODE, GRANT_TYPE_REFRESH_TOKEN)
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.resourceIds("user-rest")
.scopes("read_profile", "write_profile", "read_firstname")
.accessTokenValiditySeconds(jwtProperties.getAccessTokenExpirationTime())
.refreshTokenValiditySeconds(jwtProperties.getRefreshTokenExpirationTime());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.tokenServices(tokenServices())
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
.tokenEnhancer(jwtAccessTokenConverter())
.accessTokenConverter(jwtAccessTokenConverter())
.userDetailsService(userDetailsService);
endpoints
.pathMapping("/oauth/authorize", RESTConstants.SLASH + DomainConstants.AUTH + RESTConstants.SLASH + DomainConstants.AUTHORIZE)
.pathMapping("/oauth/token", RESTConstants.SLASH + DomainConstants.AUTH + RESTConstants.SLASH + DomainConstants.TOKEN);
}
class CustomTokenEnhancer extends JwtAccessTokenConverter {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
User user = (User) authentication.getPrincipal();
Map<String, Object> info = new LinkedHashMap<String, Object>(accessToken.getAdditionalInformation());
info.put("email", user.getEmail());
info.put(CommonConstants.JWT_CLAIM_USER_EMAIL, user.getEmail().getEmailAddress());
info.put(CommonConstants.JWT_CLAIM_USER_FULLNAME, user.getFirstname() + " " + user.getLastname());
info.put("scopes", authentication.getAuthorities().stream().map(s -> s.toString()).collect(Collectors.toList()));
DefaultOAuth2AccessToken customAccessToken = new DefaultOAuth2AccessToken(accessToken);
customAccessToken.setAdditionalInformation(info);
customAccessToken.setExpiration(tokenAuthenticationService.getExpirationDate());
return super.enhance(customAccessToken, authentication);
}
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new CustomTokenEnhancer();
jwtAccessTokenConverter.setKeyPair(new KeyStoreKeyFactory(new ClassPathResource(jwtProperties.getSslKeystoreFilename()), jwtProperties.getSslKeystorePassword().toCharArray()).getKeyPair(jwtProperties.getSslKeyPair()));
return jwtAccessTokenConverter;
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());
return defaultTokenServices;
}
}

更新:我在https://stackoverflow.com/questions/39424176/spring-security-oauth2-insufficientauthenticationexception中看到,/oauth/authorize端点应该由Spring Security通过重定向到登录页面来保护,以便在授予对/oauth/authorize端点的访问权限之前对用户进行身份验证。但是,如果授权服务器是无状态的呢?我们是否可以将此授权服务器作为API服务器?那么如何"登录"用户呢?这就是我打开首先是/oauth/authorize端点。但打开它后,会显示此User must be authenticated with Spring Security before authorization can be completed.消息。

问题是我需要保护/oauth/authorize端点。所以我这样做了,然后使用用户登录中的访问令牌,我可以使用该访问令牌向/oauth/authorize端点发送请求,并作为回报接收询问范围确认的html表单。

相关内容

最新更新