У вас есть две опции:
1-
Установка UserDetailsService экземпляр в DefaultUserAuthenticationConverter и установить преобразователь на JwtAccessTokenConverter поэтому, когда spring вызывает extractAuthentication метод из DefaultUserAuthenticationConverter, он находит (userDetailsService! = null) , поэтому он получает весь UserDetails объект, вызывая реализацию loadUserByUsername при вызове этой строки:
userDetailsService.loadUserByUsername ((String) map.get (USERNAME))
реализовано в следующем методе внутри класса Spring org. springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter.java , но просто добавьте его, чтобы уточнить, как Spring получает основной объект из карты (сначала получая его по имени пользователя, и если userDetailsService не имеет значение null, поэтому он получает весь объект):
//Note: This method implemented by spring but just putting it to show where spring exctract principal object and how extracting it
public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(USERNAME)) {
Object principal = map.get(USERNAME);
Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
if (userDetailsService != null) {
UserDetails user = userDetailsService.loadUserByUsername((String) map.get(USERNAME));
authorities = user.getAuthorities();
principal = user;
}
return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities);
}
return null;
}
Итак, что вам нужно реализовать в вашем микросервисе:
@Bean//this method just used with token store bean example: new JwtTokenStore(tokenEnhancer());
public JwtAccessTokenConverter tokenEnhancer() {
/**
* CustomTokenConverter is a class extends JwtAccessTokenConverter
* which override "enhance" to add extra information to OAuth2AccessToken after
* authenticate the user and get it by loadUserByUsername implementation
* like profileId in your case
**/
JwtAccessTokenConverter converter = new CustomTokenConverter();
DefaultAccessTokenConverter datc = new DefaultAccessTokenConverter();
datc.setUserTokenConverter(userAuthenticationConverter());
converter.setAccessTokenConverter(datc);
//Other method code implementation....
}
@Autowired
private UserDetailsService userDetailsService;
@Bean
public UserAuthenticationConverter userAuthenticationConverter() {
DefaultUserAuthenticationConverter duac = new DefaultUserAuthenticationConverter();
duac.setUserDetailsService(userDetailsService);
return duac;
}
Примечание: этот первый способ будет попадать в базу данных при каждом запросе, поэтому он загружает пользователя по имени пользователя и получает объект UserDetails, поэтому он назначает его основному объекту при аутентификации.
2-
Если по какой-либо причине вы видите, что лучше не попадать в базу данных при каждом запросе и нет проблем с выполнением данных, таких как profileId из токена, переданного в запросе.
Предполагая, что вы знаете, что старыйПолномочия, назначенные пользователю при создании токена oauth2, всегда будут в токене до тех пор, пока он не станет недействительным, даже после того, как вы измените его в базе данных для пользователя, который передает токен в запросе, чтобы пользователь мог вызвать метод, который ему больше не разрешен после извлечения токена, и онбыло разрешено до извлечения токена.
Таким образом, это означает, что если права пользователя изменились после создания токена, новые права доступа не будут проверяться @PreAuthorize, так как он не удален или не добавлен в токен, и вам придется подождать до старого токенастановится недействительным или истек срок действия, поэтому пользователь вынужден выполнить tСлужба снова для получения нового токена oauth.
В любом случае, во втором варианте вам нужно только переопределить extractAuthentication метод внутри CustomTokenConverter класс расширяет JwtAccessTokenConverter и забудьте о настройке конвертера токенов доступа converter.setAccessTokenConverter из tokenEnhancer () метод в первом варианте, и вот весь CustomTokenConverter, который вы можете использовать для чтения данных из токена и возврата принципалаобъект не просто строка username:
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
public class CustomTokenConverter extends JwtAccessTokenConverter {
// This is the method you need to override to read data direct from token passed in request
@Override
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
OAuth2Authentication authentication = super.extractAuthentication(map);
Object userIdObj = map.get(AuthenticationUtils.USER_ID);
UUID userId = userIdObj != null ? UUID.fromString(userIdObj.toString()) : null;
Object profileIdObj = map.get(AuthenticationUtils.PROFILE_ID);
UUID profileId = profileIdObj != null ? UUID.fromString(profileIdObj.toString()) : null;
Object firstNameObj = map.get(AuthenticationUtils.FIRST_NAME);
String firstName = firstNameObj != null ? String.valueOf(firstNameObj) : null;
Object lastNameObj = map.get(AuthenticationUtils.LAST_NAME);
String lastName = lastNameObj != null ? String.valueOf(lastNameObj) : null;
JwtUser principal = new JwtUser(userId, profileId, authentication.getUserAuthentication().getName(), "N/A", authentication.getUserAuthentication().getAuthorities(), firstName, lastName);
authentication = new OAuth2Authentication(authentication.getOAuth2Request(),
new UsernamePasswordAuthenticationToken(principal, "N/A", authentication.getUserAuthentication().getAuthorities()));
return authentication;
}
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
JwtUser user = (JwtUser) authentication.getPrincipal();
Map<String, Object> info = new LinkedHashMap<>(accessToken.getAdditionalInformation());
if (user.getId() != null)
info.put(AuthenticationUtils.USER_ID, user.getId());
if (user.getProfileId() != null)
info.put(AuthenticationUtils.PROFILE_ID, user.getProfileId());
if (isNotNullNotEmpty(user.getFirstName()))
info.put(AuthenticationUtils.FIRST_NAME, user.getFirstName());
if (isNotNullNotEmpty(user.getLastName()))
info.put(AuthenticationUtils.LAST_NAME, user.getLastName());
DefaultOAuth2AccessToken customAccessToken = new DefaultOAuth2AccessToken(accessToken);
customAccessToken.setAdditionalInformation(info);
return super.enhance(customAccessToken, authentication);
}
private boolean isNotNullNotEmpty(String str) {
return Optional.ofNullable(str).map(String::trim).map(string -> !str.isEmpty()).orElse(false);
}
}
Наконец: угадайте, откуда я знаю, что вы спрашиваете о JWT, используемом с OAuth2?
Потому что я частьвашей компании: P, и вы знаете, что: P