赞
踩
原本的OAuth2登录支持用户名密码登录,现在还想支持另外用id号码和密码登录。但是OAuth2默认提供的UserDetailsService只允许传入一个参数:
想要实现多种用户登录,是不是可以考虑loadUserByUsername方法携带多个参数呢?
接下来记录一下实现步骤:
新增interface CustomUserDetailsServiceInter 继承原来的UserDetailsService,新增自定义方法loadUserByUsername
- public interface CustomUserDetailsServiceInter extends UserDetailsService {
-
- CustomUser loadUserByUsername(String var1, String var2) throws UsernameNotFoundException;
-
- }
然后根据自己需要实现CustomUserDetailsServiceInter接口的方法,这里就不放出来了
- @Slf4j
- @Component
- public class CustomUserDetailsService implements CustomUserDetailsServiceInter {
-
- @Override
- public CustomUser loadUserByUsername(String username, String idCard) throws UsernameNotFoundException {
- // 根据自己需要进行实现
- // 1.获取用户
- // 2.获取用户可访问权限信息
- // 3.构造UserDetails信息并返回
- return userDetails;
- }
- }
从现在开始,所有需要用到userDetailsService的,全部都要替换成自定义CustomUserDetailsService。比如WebSecurityConfigurer、AuthServerConfigurer
复制org.springframework.security.authentication.dao.DaoAuthenticationProvider的代码,自定义 CustomerDaoAuthenticationProvider,然后进行修改retrieveUser()方法,其他不需要动
public class CustomerDaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword"; private PasswordEncoder passwordEncoder; private volatile String userNotFoundEncodedPassword; private CustomUserDetailsService userDetailsService; private UserDetailsPasswordService userDetailsPasswordService; public CustomerDaoAuthenticationProvider() { setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()); } @Override @SuppressWarnings("deprecation") protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { if (authentication.getCredentials() == null) { this.logger.debug("Failed to authenticate since no credentials provided"); throw new BadCredentialsException(this.messages .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } String presentedPassword = authentication.getCredentials().toString(); String password = userDetails.getPassword(); boolean matches = this.passwordEncoder.matches(presentedPassword, password); if (!matches) { this.logger.debug("Failed to authenticate since password does not match stored value"); throw new BadCredentialsException(this.messages .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } } @Override protected void doAfterPropertiesSet() { Assert.notNull(this.userDetailsService, "A UserDetailsService must be set"); } /*@Override protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { prepareTimingAttackProtection(); try { UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username, authentication.getDetails()); if (loadedUser == null) { throw new InternalAuthenticationServiceException( "UserDetailsService returned null, which is an interface contract violation"); } return loadedUser; } catch (UsernameNotFoundException ex) { mitigateAgainstTimingAttack(authentication); throw ex; } catch (InternalAuthenticationServiceException ex) { throw ex; } catch (Exception ex) { throw new InternalAuthenticationServiceException(ex.getMessage(), ex); } }*/ @Override protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) { boolean upgradeEncoding = this.userDetailsPasswordService != null && this.passwordEncoder.upgradeEncoding(user.getPassword()); if (upgradeEncoding) { String presentedPassword = authentication.getCredentials().toString(); String newPassword = this.passwordEncoder.encode(presentedPassword); user = this.userDetailsPasswordService.updatePassword(user, newPassword); } return super.createSuccessAuthentication(principal, authentication, user); } private void prepareTimingAttackProtection() { if (this.userNotFoundEncodedPassword == null) { this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD); } } private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) { if (authentication.getCredentials() != null) { String presentedPassword = authentication.getCredentials().toString(); this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword); } } /** * Sets the PasswordEncoder instance to be used to encode and validate passwords. If * not set, the password will be compared using * {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()} * * @param passwordEncoder must be an instance of one of the {@code PasswordEncoder} * types. */ public void setPasswordEncoder(PasswordEncoder passwordEncoder) { Assert.notNull(passwordEncoder, "passwordEncoder cannot be null"); this.passwordEncoder = passwordEncoder; this.userNotFoundEncodedPassword = null; } protected PasswordEncoder getPasswordEncoder() { return this.passwordEncoder; } public void setUserDetailsService(CustomUserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } protected CustomUserDetailsService getUserDetailsService() { return this.userDetailsService; } public void setUserDetailsPasswordService(UserDetailsPasswordService userDetailsPasswordService) { this.userDetailsPasswordService = userDetailsPasswordService; } protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { this.prepareTimingAttackProtection(); Map<String, String> map = (Map<String, String>) authentication.getDetails(); // 自定义添加 try { String userIdCard = map.get("idCard"); // 自定义添加 UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username, userIdCard); if (loadedUser == null) { throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation"); } else { return loadedUser; } } catch (UsernameNotFoundException var4) { this.mitigateAgainstTimingAttack(authentication); throw var4; } catch (InternalAuthenticationServiceException var5) { throw var5; } catch (Exception var6) { throw new InternalAuthenticationServiceException(var6.getMessage(), var6); } } }
记得将自定义的CustomerDaoAuthenticationProvider中的userDetailsService替换成自定义的CustomUserDetailsService
到WebSecurityConfig配置上面的CustomAuthenticationProvider
- @Bean(name="customAuthenticationProvider")
- public AuthenticationProvider customAuthenticationProvider() {
- CustomerDaoAuthenticationProvider customAuthenticationProvider= new CustomerDaoAuthenticationProvider();
- customAuthenticationProvider.setUserDetailsService(userDetailsService);
- customAuthenticationProvider.setHideUserNotFoundExceptions(false);
- customAuthenticationProvider.setPasswordEncoder(passwordEncoder);
- return customAuthenticationProvider;
- }
-
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- auth.authenticationProvider(customAuthenticationProvider());
- }
可以去获取token试试了
获取access_token请求(/oauth/token)
请求所需参数:client_id、client_secret、grant_type、username、password 这些是默认的,可以用需要的idCard替换username
现在去请求刷新Token
刷新token请求(/oauth/token)
请求所需参数:grant_type、refresh_token、client_id、client_secret其中grant_type为固定值:grant_type=refresh_token
会发现怎样也刷新不了,因为刷新Token时用的还是原版的UserDetailsService,执行的还是单个参数的loadUserByUsername,所以下面要重新自定义相关类来替换掉
复制org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper 自定义 CustomUserDetailsByNameServiceWrapper 在 loadUserDetails() 方法添加自定义的idCard
public class CustomUserDetailsByNameServiceWrapper<T extends Authentication> implements AuthenticationUserDetailsService<T>, InitializingBean { private CustomUserDetailsService userDetailsService = null; /** * Constructs an empty wrapper for compatibility with Spring Security 2.0.x's method * of using a setter. */ public CustomUserDetailsByNameServiceWrapper() { // constructor for backwards compatibility with 2.0 } /** * Constructs a new wrapper using the supplied * {@link org.springframework.security.core.userdetails.UserDetailsService} as the * service to delegate to. * * @param userDetailsService the UserDetailsService to delegate to. */ public CustomUserDetailsByNameServiceWrapper(final CustomUserDetailsService userDetailsService) { Assert.notNull(userDetailsService, "userDetailsService cannot be null."); this.userDetailsService = userDetailsService; } /** * Check whether all required properties have been set. * * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ public void afterPropertiesSet() { Assert.notNull(this.userDetailsService, "UserDetailsService must be set"); } /** * Get the UserDetails object from the wrapped UserDetailsService implementation */ public UserDetails loadUserDetails(T authentication) throws UsernameNotFoundException { // ----------添加自定义的内容---------- AbstractAuthenticationToken principal = (AbstractAuthenticationToken) authentication.getPrincipal(); Map<String,String> map = (Map<String, String>) principal.getDetails(); String userIdCard = map.get("idCard"); // ----------添加自定义的内容---------- return this.userDetailsService.loadUserByUsername(authentication.getName(), userIdCard); // 使用自定义的userDetailsService } /** * Set the wrapped UserDetailsService implementation * * @param aUserDetailsService The wrapped UserDetailsService to set */ public void setUserDetailsService(CustomUserDetailsService aUserDetailsService) { this.userDetailsService = aUserDetailsService; } }
复制 org.springframework.security.oauth2.provider.token.DefaultTokenServices 到自定义的 CustomTokenServices 然后修改refreshAccessToken() 方法的if (this.authenticationManager != null && !authentication.isClientOnly()) 这里面的内容
public class CustomTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices, ConsumerTokenServices, InitializingBean { private int refreshTokenValiditySeconds = 60 * 60 * 24 * 30; // default 30 days. private int accessTokenValiditySeconds = 60 * 60 * 12; // default 12 hours. private boolean supportRefreshToken = false; private boolean reuseRefreshToken = true; private TokenStore tokenStore; private ClientDetailsService clientDetailsService; private TokenEnhancer accessTokenEnhancer; private AuthenticationManager authenticationManager; /** * Initialize these token services. If no random generator is set, one will be created. */ public void afterPropertiesSet() throws Exception { Assert.notNull(tokenStore, "tokenStore must be set"); } @Transactional public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException { OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication); OAuth2RefreshToken refreshToken = null; if (existingAccessToken != null) { if (existingAccessToken.isExpired()) { if (existingAccessToken.getRefreshToken() != null) { refreshToken = existingAccessToken.getRefreshToken(); // The token store could remove the refresh token when the // access token is removed, but we want to // be sure... tokenStore.removeRefreshToken(refreshToken); } tokenStore.removeAccessToken(existingAccessToken); } else { // Re-store the access token in case the authentication has changed tokenStore.storeAccessToken(existingAccessToken, authentication); return existingAccessToken; } } // Only create a new refresh token if there wasn't an existing one // associated with an expired access token. // Clients might be holding existing refresh tokens, so we re-use it in // the case that the old access token // expired. if (refreshToken == null) { refreshToken = createRefreshToken(authentication); } // But the refresh token itself might need to be re-issued if it has // expired. else if (refreshToken instanceof ExpiringOAuth2RefreshToken) { ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken; if (System.currentTimeMillis() > expiring.getExpiration().getTime()) { refreshToken = createRefreshToken(authentication); } } OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken); tokenStore.storeAccessToken(accessToken, authentication); // In case it was modified refreshToken = accessToken.getRefreshToken(); if (refreshToken != null) { tokenStore.storeRefreshToken(refreshToken, authentication); } return accessToken; } @Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class}) public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest) throws AuthenticationException { if (!supportRefreshToken) { throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue); } OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue); if (refreshToken == null) { throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue); } OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken); if (this.authenticationManager != null && !authentication.isClientOnly()) { /*// The client has already been authenticated, but the user authentication might be old now, so give it a // chance to re-authenticate. Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities()); user = authenticationManager.authenticate(user); Object details = authentication.getDetails(); authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user); authentication.setDetails(details);*/ // OAuth2Authentication 中的 Authentication userAuthentication 丢失了 Detail的信息,需要补上 // 1.从tokenRequest中获取请求的信息,并重新构造成 UsernamePasswordAuthenticationToken // 2.设置好了Detail的信息再传入构造 PreAuthenticatedAuthenticationToken 交由后面的验证 //tokenRequest.getRequestParameters(); Object details = tokenRequest.getRequestParameters(); UsernamePasswordAuthenticationToken userAuthentication = (UsernamePasswordAuthenticationToken) authentication.getUserAuthentication(); userAuthentication.setDetails(details); // 去掉原来的,使用自己重新构造的 userAuthentication // Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities()); Authentication user = new PreAuthenticatedAuthenticationToken(userAuthentication, "", authentication.getAuthorities()); user = this.authenticationManager.authenticate(user); authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user); authentication.setDetails(details); } String clientId = authentication.getOAuth2Request().getClientId(); if (clientId == null || !clientId.equals(tokenRequest.getClientId())) { throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue); } // clear out any access tokens already associated with the refresh // token. tokenStore.removeAccessTokenUsingRefreshToken(refreshToken); if (isExpired(refreshToken)) { tokenStore.removeRefreshToken(refreshToken); throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken); } authentication = createRefreshedAuthentication(authentication, tokenRequest); if (!reuseRefreshToken) { tokenStore.removeRefreshToken(refreshToken); refreshToken = createRefreshToken(authentication); } OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken); tokenStore.storeAccessToken(accessToken, authentication); if (!reuseRefreshToken) { tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication); } return accessToken; } public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) { return tokenStore.getAccessToken(authentication); } /** * Create a refreshed authentication. * * @param authentication The authentication. * @param request The scope for the refreshed token. * @return The refreshed authentication. * @throws InvalidScopeException If the scope requested is invalid or wider than the original scope. */ private OAuth2Authentication createRefreshedAuthentication(OAuth2Authentication authentication, TokenRequest request) { OAuth2Authentication narrowed = authentication; Set<String> scope = request.getScope(); OAuth2Request clientAuth = authentication.getOAuth2Request().refresh(request); if (scope != null && !scope.isEmpty()) { Set<String> originalScope = clientAuth.getScope(); if (originalScope == null || !originalScope.containsAll(scope)) { throw new InvalidScopeException("Unable to narrow the scope of the client authentication to " + scope + ".", originalScope); } else { clientAuth = clientAuth.narrowScope(scope); } } narrowed = new OAuth2Authentication(clientAuth, authentication.getUserAuthentication()); return narrowed; } protected boolean isExpired(OAuth2RefreshToken refreshToken) { if (refreshToken instanceof ExpiringOAuth2RefreshToken) { ExpiringOAuth2RefreshToken expiringToken = (ExpiringOAuth2RefreshToken) refreshToken; return expiringToken.getExpiration() == null || System.currentTimeMillis() > expiringToken.getExpiration().getTime(); } return false; } public OAuth2AccessToken readAccessToken(String accessToken) { return tokenStore.readAccessToken(accessToken); } public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException, InvalidTokenException { OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue); if (accessToken == null) { throw new InvalidTokenException("Invalid access token: " + accessTokenValue); } else if (accessToken.isExpired()) { tokenStore.removeAccessToken(accessToken); throw new InvalidTokenException("Access token expired: " + accessTokenValue); } OAuth2Authentication result = tokenStore.readAuthentication(accessToken); if (result == null) { // in case of race condition throw new InvalidTokenException("Invalid access token: " + accessTokenValue); } if (clientDetailsService != null) { String clientId = result.getOAuth2Request().getClientId(); try { clientDetailsService.loadClientByClientId(clientId); } catch (ClientRegistrationException e) { throw new InvalidTokenException("Client not valid: " + clientId, e); } } return result; } public String getClientId(String tokenValue) { OAuth2Authentication authentication = tokenStore.readAuthentication(tokenValue); if (authentication == null) { throw new InvalidTokenException("Invalid access token: " + tokenValue); } OAuth2Request clientAuth = authentication.getOAuth2Request(); if (clientAuth == null) { throw new InvalidTokenException("Invalid access token (no client id): " + tokenValue); } return clientAuth.getClientId(); } public boolean revokeToken(String tokenValue) { OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue); if (accessToken == null) { return false; } if (accessToken.getRefreshToken() != null) { tokenStore.removeRefreshToken(accessToken.getRefreshToken()); } tokenStore.removeAccessToken(accessToken); return true; } private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) { if (!isSupportRefreshToken(authentication.getOAuth2Request())) { return null; } int validitySeconds = getRefreshTokenValiditySeconds(authentication.getOAuth2Request()); String value = UUID.randomUUID().toString(); if (validitySeconds > 0) { return new DefaultExpiringOAuth2RefreshToken(value, new Date(System.currentTimeMillis() + (validitySeconds * 1000L))); } return new DefaultOAuth2RefreshToken(value); } private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) { DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString()); int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request()); if (validitySeconds > 0) { token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L))); } token.setRefreshToken(refreshToken); token.setScope(authentication.getOAuth2Request().getScope()); return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token; } /** * The access token validity period in seconds * * @param clientAuth the current authorization request * @return the access token validity period in seconds */ protected int getAccessTokenValiditySeconds(OAuth2Request clientAuth) { if (clientDetailsService != null) { ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId()); Integer validity = client.getAccessTokenValiditySeconds(); if (validity != null) { return validity; } } return accessTokenValiditySeconds; } /** * The refresh token validity period in seconds * * @param clientAuth the current authorization request * @return the refresh token validity period in seconds */ protected int getRefreshTokenValiditySeconds(OAuth2Request clientAuth) { if (clientDetailsService != null) { ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId()); Integer validity = client.getRefreshTokenValiditySeconds(); if (validity != null) { return validity; } } return refreshTokenValiditySeconds; } /** * Is a refresh token supported for this client (or the global setting if * {@link #setClientDetailsService(ClientDetailsService) clientDetailsService} is not set. * * @param clientAuth the current authorization request * @return boolean to indicate if refresh token is supported */ protected boolean isSupportRefreshToken(OAuth2Request clientAuth) { if (clientDetailsService != null) { ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId()); return client.getAuthorizedGrantTypes().contains("refresh_token"); } return this.supportRefreshToken; } /** * An access token enhancer that will be applied to a new token before it is saved in the token store. * * @param accessTokenEnhancer the access token enhancer to set */ public void setTokenEnhancer(TokenEnhancer accessTokenEnhancer) { this.accessTokenEnhancer = accessTokenEnhancer; } /** * The validity (in seconds) of the refresh token. If less than or equal to zero then the tokens will be * non-expiring. * * @param refreshTokenValiditySeconds The validity (in seconds) of the refresh token. */ public void setRefreshTokenValiditySeconds(int refreshTokenValiditySeconds) { this.refreshTokenValiditySeconds = refreshTokenValiditySeconds; } /** * The default validity (in seconds) of the access token. Zero or negative for non-expiring tokens. If a client * details service is set the validity period will be read from the client, defaulting to this value if not defined * by the client. * * @param accessTokenValiditySeconds The validity (in seconds) of the access token. */ public void setAccessTokenValiditySeconds(int accessTokenValiditySeconds) { this.accessTokenValiditySeconds = accessTokenValiditySeconds; } /** * Whether to support the refresh token. * * @param supportRefreshToken Whether to support the refresh token. */ public void setSupportRefreshToken(boolean supportRefreshToken) { this.supportRefreshToken = supportRefreshToken; } /** * Whether to reuse refresh tokens (until expired). * * @param reuseRefreshToken Whether to reuse refresh tokens (until expired). */ public void setReuseRefreshToken(boolean reuseRefreshToken) { this.reuseRefreshToken = reuseRefreshToken; } /** * The persistence strategy for token storage. * * @param tokenStore the store for access and refresh tokens. */ public void setTokenStore(TokenStore tokenStore) { this.tokenStore = tokenStore; } /** * An authentication manager that will be used (if provided) to check the user authentication when a token is * refreshed. * * @param authenticationManager the authenticationManager to set */ public void setAuthenticationManager(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; } /** * The client details service to use for looking up clients (if necessary). Optional if the access token expiry is * set globally via {@link #setAccessTokenValiditySeconds(int)}. * * @param clientDetailsService the client details service */ public void setClientDetailsService(ClientDetailsService clientDetailsService) { this.clientDetailsService = clientDetailsService; } }
到认证服务器配置类 AuthorizationServerConfig 配置刚刚自定义的两个类 CustomUserDetailsByNameServiceWrapper 和 CustomTokenServices
@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(jwtTokenStore()) // 根据自己需要 .tokenEnhancer(jwtAccessTokenConverter()) .reuseRefreshTokens(true) .authenticationManager(authenticationManager) .userDetailsService(userDetailsService) .tokenServices(customTokenServices(endpoints)); // 自定义TokenServices } public CustomTokenServices customTokenServices(AuthorizationServerEndpointsConfigurer endpoints){ CustomTokenServices tokenServices = new CustomTokenServices(); tokenServices.setTokenStore(endpoints.getTokenStore()); tokenServices.setSupportRefreshToken(true); tokenServices.setReuseRefreshToken(true); tokenServices.setClientDetailsService(clientDetails()); tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer()); // 设置自定义的CustomUserDetailsByNameServiceWrapper if (userDetailsService != null) { PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider(); provider.setPreAuthenticatedUserDetailsService(new CustomUserDetailsByNameServiceWrapper(userDetailsService)); tokenServices.setAuthenticationManager(new ProviderManager(Arrays.asList(provider))); } return tokenServices; }
至此,可以刷新token试试了。
在获取Token的时候,像client_id,client_secret等默认的请求参数,框架会自动放到details中,但是在刷新token的时候不知什么原因,details中并没有放入请求的参数,所以需要自己重新构造,还好这些信息都保存在tokenRequest中,所以new一个UsernamePasswordAuthenticationToken,把它们都放进details中,再传入 PreAuthenticatedAuthenticationToken,后续就交由框架去验证就可以了 。这一步操作就是CustomTokenServices类refreshAccessToken方法
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。