В настоящее время я нахожусь в процессе введения OAuth в мое приложение Spring, но мне не повезло, чтобы правильно его интегрировать Я использую Spring Boot 2 .
Мои требования:
- Сервер авторизации и ресурсов - это одно и то же приложение , работающее на одном сервере
- Неявный, код авторизации и полномочия владельца ресурса необходимо поддерживать потоки
- API, который я хочу защитить жизни в "/ api / v1 /"
Ни один из потоков не работает правильно. То, чего я достиг до сих пор, основано на учебном пособии https://www.devglan.com/spring-security/spring-oauth2-role-based-authorization и ответе: https://stackoverflow.com/a/52386009/4454752
Так что мой Сервер авторизации выглядит так:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
@Autowired
public AuthorizationServerConfig(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("as466gf");
return converter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("my-client-id")
.authorizedGrantTypes("authorization_code", "implicit", "refresh_token", "password")
.authorities("ADMIN")
.scopes("all")
.resourceIds("product_api")
.secret("$2a$10$jfAHmk4szDU/t1qLGlFTLukuBZL0ZHZGUJQICePjjyq6IrLOS934.")
.redirectUris("https://example.com")
.accessTokenValiditySeconds(7200)
.refreshTokenValiditySeconds(7200);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(authenticationManager)
.accessTokenConverter(accessTokenConverter());
}
}
Тогда ResourceServer
@Configuration
@EnableResourceServer
@Order(2)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId("product_api");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/**")
.and().authorizeRequests()
.antMatchers("/**").permitAll()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
И, наконец, WebSecurityConfig
@Configuration
@Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource(name = "userDetailService")
private UserDetailService userDetailsService;
@Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(encoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/v1/**")
.hasAnyRole("ADMIN", "USER").and()
.httpBasic().and().formLogin().and().authorizeRequests().anyRequest().authenticated();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(encoder());
}
}
Мой UserDetails и полное управление пользователями уже настроены, поэтому здесь не нужно что-то менять.
Теперь к случаям использования.
Если доступ "/ oauth / token":
curl --request POST \
--url http://localhost:8080/oauth/token \
--header 'authorization: Basic bXktY2xpZW50Om15LXNlY3JldA==' \
--header 'content-type: application/x-www-form-urlencoded' \
--data 'grant_type=password&username=admin&password=test'
Так что с my-client: my-secret я получаю ответ с токеном доступа и токеном обновления. Но если я хочу использовать токен доступа в моем API, я получаю Access denied
. Если я проверяю токен с /oauth/check_token
, он говорит, что токен действителен.
Та же проблема, если я использую "/ oauth / authorize" (неявный поток). Я знаю, что мне нужна страница входа с весны, чтобы она работала, поэтому я добавил formLogin()
в WebSecurityConfig
. Но если я запрашиваю http://localhost:8080/oauth/authorize?response_type=token&client_id=my-client&redirect_uri=https://example.com
, меня перенаправляют на /login
, я вхожу в систему, затем получаю токен доступа из URL-адреса перенаправления, но снова, если я использую этот токен, я получаю ошибку 401
.
Конечная точка, к которой я хочу получить доступ, обрабатывается следующим контроллером:
@RestController
@RequestMapping(value = "/api/v1/user")
@CrossOrigin(origins = "*")
public class UserController {
private static final Logger LOGGER = LogManager.getLogger(UserController.class);
public static final String ROLE_USER = "ROLE_USER";
private final AuthenticationFacade authenticationFacade;
private final UserService userService;
@Autowired
public UserController(AuthenticationFacade authenticationFacade,
UserService userService) {
this.authenticationFacade = authenticationFacade;
this.userService = userService;
}
@RequestMapping(value = "/me", method = RequestMethod.GET)
public Optional<User> getCurrentUser() {
LOGGER.info("Requesting /api/v1/user/me");
return userService.findByUsername((String) authenticationFacade.getAuthentication().getPrincipal());
}
}
Я почти уверен, что что-то с настройкой безопасности испортилось, но я понятия не имею, что это может быть. Я просмотрел множество руководств в Интернете, но не нашел ни одного, в котором объясняется весь код авторизации, объединенный. Я думаю, что это может быть небольшая ошибка с аутентификацией URL, но я понятия не имею, что это может быть.
Я был бы очень рад, если бы кто-то знал ответ на этот вопрос.