Skip to content

Commit

Permalink
Merge branch 'feature/1.0.0' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
fuhouyu committed Nov 5, 2024
2 parents 49f407e + 950c65b commit 4c18dc7
Show file tree
Hide file tree
Showing 13 changed files with 247 additions and 267 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright 2024-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fuhouyu.framework.security;

import com.fuhouyu.framework.security.core.passwordencoder.PasswordEncoderFactory;
import com.fuhouyu.framework.security.core.provider.oidc.OidcAuthenticationProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;

import java.util.List;

/**
* <p>
* oidc配置
* </p>
*
* @author fuhouyu
* @since 2024/11/4 22:06
*/
@Configuration
public class AuthenticationConfiguration {

/**
* dao层实现
*
* @param passwordEncoder 密码管理器
* @param userDetailsService 用户详情接口
* @return dao默认实现
*/
@Bean
public AuthenticationProvider daoAuthenticationProvider(UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder) {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(passwordEncoder);
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
return daoAuthenticationProvider;
}

/**
* oidc provider
*
* @param userDetailsService 用户详情service
* @param clientRegistrationRepository 客户端仓库信息
* @return oidcProvider
*/
@Bean
@ConditionalOnBean({UserDetailsService.class, ClientRegistrationRepository.class})
public AuthenticationProvider oidcAuthenticationProvider(UserDetailsService userDetailsService,
ClientRegistrationRepository clientRegistrationRepository) {
return new OidcAuthenticationProvider(new DefaultOAuth2UserService(), userDetailsService, clientRegistrationRepository);
}


/**
* 认证管理器配置这里可以进行除其他登录模式的扩展,需要实现{@link AuthenticationProvider}
*
* @param authenticationProviders 认证提供者集合
* @return 认证管理器
*/
@Bean
@Primary
@ConditionalOnBean({AuthenticationProvider.class})
public AuthenticationManager authenticationManager(List<AuthenticationProvider> authenticationProviders) {
return new ProviderManager(authenticationProviders);
}

/**
* 返回sm3 密码编码器的bean,当passwordEncoder不存在时,则会创建。
*
* @return sm3 密码编码器bean
*/
@Bean
@ConditionalOnMissingBean(PasswordEncoder.class)
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactory.createDelegatingPasswordEncoder("sm3");
}


}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,15 @@

import com.fuhouyu.framework.cache.CacheAutoConfiguration;
import com.fuhouyu.framework.cache.service.CacheService;
import com.fuhouyu.framework.security.core.passwordencoder.PasswordEncoderFactory;
import com.fuhouyu.framework.security.token.TokenStore;
import com.fuhouyu.framework.security.token.TokenStoreCache;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.List;

/**
* <p>
Expand All @@ -48,7 +41,7 @@
CacheAutoConfiguration.class,
OAuth2ClientAutoConfiguration.class
})
@Import({AuthenticationProviderConfiguration.class})
@Import({AuthenticationConfiguration.class})
public class SecurityAutoConfiguration {

/**
Expand All @@ -59,33 +52,9 @@ public class SecurityAutoConfiguration {
*/
@Bean
@ConditionalOnMissingBean(TokenStore.class)
@ConditionalOnBean(CacheService.class)
public TokenStore tokenStore(CacheService<String, Object> cacheService) {
return new TokenStoreCache("user", cacheService);
}


/**
* 认证管理器配置这里可以进行除其他登录模式的扩展,需要实现{@link AuthenticationProvider}
*
* @param authenticationProviders 认证提供者集合
* @return 认证管理器
*/
@Bean
@Primary
public AuthenticationManager authenticationManager(List<AuthenticationProvider> authenticationProviders) {
return new ProviderManager(authenticationProviders);
}

/**
* 返回sm3 密码编码器的bean,当passwordEncoder不存在时,则会创建。
*
* @return sm3 密码编码器bean
*/
@Bean
@ConditionalOnMissingBean(PasswordEncoder.class)
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactory.createDelegatingPasswordEncoder("sm3");
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.fuhouyu.framework.security.core.provider.oidc;

import com.fuhouyu.framework.common.utils.LoggerUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.http.converter.FormHttpMessageConverter;
Expand Down Expand Up @@ -76,13 +77,20 @@ public class OidcAuthenticationProvider implements AuthenticationProvider {
private static final String INVALID_ID_TOKEN_ERROR_CODE = "invalid_id_token";

private static final String INVALID_NONCE_ERROR_CODE = "invalid_nonce";

private static final MediaType APPLICATION_FORM_URLENCODED_UTF8 = new MediaType(
MediaType.APPLICATION_FORM_URLENCODED, StandardCharsets.UTF_8);
private final GrantedAuthoritiesMapper authoritiesMapper = ((authorities) -> authorities);

private final GrantedAuthoritiesMapper authoritiesMapper = authorities -> authorities;

private final UserDetailsService userDetailsService;

private final OAuth2UserService<OAuth2UserRequest, OAuth2User> userService;

private final ClientRegistrationRepository clientRegistrationRepository;

private final JwtDecoderFactory<ClientRegistration> jwtDecoderFactory = new OidcIdTokenDecoderFactory();

private final RestOperations restOperations;

public OidcAuthenticationProvider(OAuth2UserService<OAuth2UserRequest, OAuth2User> userService,
Expand Down Expand Up @@ -110,17 +118,14 @@ public Authentication authenticate(Authentication authentication) throws Authent
if (Objects.isNull(clientRegistration)) {
throw new IllegalArgumentException("invalid client id " + oidcAuthenticationToken.getClientId());
}
RequestEntity<?> requestEntity = this.createRequestEntity(clientRegistration);
RequestEntity<?> requestEntity = this.createRequestEntity(clientRegistration, oidcAuthenticationToken);
OAuth2AccessTokenResponse accessTokenResponse = this.getResponse(requestEntity);
Map<String, Object> additionalParameters = this.getAdditionalParameters(accessTokenResponse, clientRegistration);
OidcIdToken idToken = createOidcToken(clientRegistration, accessTokenResponse);
validateNonce(oidcAuthenticationToken.getNonce(), idToken);
OAuth2User oidcUser = this.userService.loadUser(new OidcUserRequest(clientRegistration,
accessTokenResponse.getAccessToken(), idToken, additionalParameters));
UserDetails userDetails = this.userDetailsService.loadUserByUsername(oidcUser.getName());
if (Objects.isNull(userDetails)) {
throw new IllegalArgumentException("系统中的用户不存在");
}
Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper
.mapAuthorities(oidcUser.getAuthorities());
OidcAuthenticationToken result = new OidcAuthenticationToken(
Expand Down Expand Up @@ -215,28 +220,36 @@ private OAuth2AccessTokenResponse getResponse(RequestEntity<?> request) {
ResponseEntity<OAuth2AccessTokenResponse> tokenResponse = this.restOperations.exchange(request, OAuth2AccessTokenResponse.class);
Assert.notNull(tokenResponse,
"The authorization server responded to this Authorization Code grant request with an empty body; as such, it cannot be materialized into an OAuth2AccessTokenResponse instance. Please check the HTTP response code in your server logs for more details.");
return tokenResponse.getBody();
OAuth2AccessTokenResponse responseBody = tokenResponse.getBody();
Assert.notNull(responseBody, "Response Body is null");
return responseBody;
} catch (RestClientException ex) {
OAuth2Error oauth2Error = new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE,
"An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: "
+ ex.getMessage(),
null);
throw new OAuth2AuthorizationException(oauth2Error, ex);
} catch (OAuth2AuthorizationException e) {
LoggerUtil.error(log, "第三方平台使用失败,entity:{} , 失败原因:{}",
request, e.getError());
throw new IllegalArgumentException("第三方平台登录失败");
}
}

/**
* 创建请求实体
*
* @param clientRegistration 客户端注册信息
* @param oidcAuthenticationToken oidc 认证token
* @return 请求实体
*/
private RequestEntity<?> createRequestEntity(ClientRegistration clientRegistration) {
private RequestEntity<?> createRequestEntity(ClientRegistration clientRegistration,
OidcAuthenticationToken oidcAuthenticationToken) {
URI uri = UriComponentsBuilder
.fromUriString(clientRegistration.getProviderDetails().getTokenUri())
.build()
.toUri();
return new RequestEntity<>(this.createParameters(clientRegistration),
return new RequestEntity<>(this.createParameters(clientRegistration, oidcAuthenticationToken),
this.createHttpHeaders(clientRegistration), HttpMethod.POST, uri);
}

Expand All @@ -250,6 +263,7 @@ private HttpHeaders createHttpHeaders(ClientRegistration clientRegistration) {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(List.of(APPLICATION_JSON));
headers.setContentType(APPLICATION_FORM_URLENCODED_UTF8);

if (ClientAuthenticationMethod.CLIENT_SECRET_BASIC.equals(clientRegistration.getClientAuthenticationMethod())) {
String clientId = URLEncoder.encode(clientRegistration.getClientId(), StandardCharsets.UTF_8);
String clientSecret = URLEncoder.encode(clientRegistration.getClientSecret(), StandardCharsets.UTF_8);
Expand All @@ -262,11 +276,14 @@ private HttpHeaders createHttpHeaders(ClientRegistration clientRegistration) {
* 设置参数
*
* @param clientRegistration 客户端注册信息
* @param oidcAuthenticationToken oidc token
* @return 参数对象
*/
private MultiValueMap<String, String> createParameters(ClientRegistration clientRegistration) {
private MultiValueMap<String, String> createParameters(ClientRegistration clientRegistration,
OidcAuthenticationToken oidcAuthenticationToken) {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue());
parameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());
parameters.set(OAuth2ParameterNames.CODE, oidcAuthenticationToken.getCode());
if (!ClientAuthenticationMethod.CLIENT_SECRET_BASIC
.equals(clientRegistration.getClientAuthenticationMethod())) {
parameters.set(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.fuhouyu.framework.security.core.provider.oidc;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
Expand All @@ -39,6 +40,7 @@
@Getter
@Setter
@ToString
@EqualsAndHashCode(callSuper = true)
public class OidcAuthenticationToken extends AbstractAuthenticationToken {

private final String code;
Expand All @@ -47,11 +49,11 @@ public class OidcAuthenticationToken extends AbstractAuthenticationToken {

private final String clientId;

private final transient OAuth2User principal;
private transient OAuth2User principal;

private final OAuth2AccessToken accessToken;
private OAuth2AccessToken accessToken;

private final OAuth2RefreshToken refreshToken;
private OAuth2RefreshToken refreshToken;

private String nonce;

Expand All @@ -68,11 +70,16 @@ public OidcAuthenticationToken(String code, String state,
this.principal = principal;
this.accessToken = accessToken;
this.refreshToken = refreshToken;
this.setAuthenticated(true);
}


public OidcAuthenticationToken(String code, String state, String clientId) {
this(code, state, clientId, null, Collections.emptyList(), null, null);
super(Collections.emptyList());
this.code = code;
this.state = state;
this.clientId = clientId;
this.setAuthenticated(false);
}

@Override
Expand Down
Loading

0 comments on commit 4c18dc7

Please sign in to comment.