diff --git a/oidc/src/main/java/com/tuoheng/oauth/oidc/config/SecurityConfig.java b/oidc/src/main/java/com/tuoheng/oauth/oidc/config/SecurityConfig.java index 1acb953..e78ed6d 100644 --- a/oidc/src/main/java/com/tuoheng/oauth/oidc/config/SecurityConfig.java +++ b/oidc/src/main/java/com/tuoheng/oauth/oidc/config/SecurityConfig.java @@ -5,6 +5,7 @@ import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jose.jwk.source.ImmutableJWKSet; import com.nimbusds.jose.jwk.source.JWKSource; import com.nimbusds.jose.proc.SecurityContext; +import com.tuoheng.oauth.oidc.filter.ForcePromptLoginFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -14,6 +15,8 @@ import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; @@ -47,12 +50,24 @@ import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.time.Duration; import java.util.Arrays; +import java.util.Objects; import java.util.UUID; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; @Configuration public class SecurityConfig { + + @Bean + public FilterRegistrationBean forcePromptLoginFilterRegistration(ForcePromptLoginFilter filter) { + FilterRegistrationBean registration = new FilterRegistrationBean<>(); + registration.setFilter(filter); + registration.addUrlPatterns("/oauth2/authorize"); + registration.setOrder(-101); // 顺序要比Spring Security的Filter更靠前 + return registration; + } @Autowired @Lazy AuthenticationProvider tenantAwareAuthenticationProvider; @@ -62,10 +77,11 @@ public class SecurityConfig { super(loginFormUrl); } + + @Override protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response, org.springframework.security.core.AuthenticationException exception) { String loginUrl = super.determineUrlToUseForThisRequest(request, response, exception); - // 获取client_id参数 String clientId = request.getParameter("client_id"); if (clientId != null && !clientId.isEmpty()) { diff --git a/oidc/src/main/java/com/tuoheng/oauth/oidc/filter/ForcePromptLoginFilter.java b/oidc/src/main/java/com/tuoheng/oauth/oidc/filter/ForcePromptLoginFilter.java new file mode 100644 index 0000000..4955a0a --- /dev/null +++ b/oidc/src/main/java/com/tuoheng/oauth/oidc/filter/ForcePromptLoginFilter.java @@ -0,0 +1,46 @@ +package com.tuoheng.oauth.oidc.filter; + + + +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +@Component +public class ForcePromptLoginFilter implements Filter { + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + HttpServletResponse response = (HttpServletResponse) servletResponse; + System.out.println("ForcePromptLoginFilter invoked! URI=" + request.getRequestURI()); +// // 只拦截/oauth2/authorize请求 +// String uri = request.getRequestURI(); +// if ("/oauth2/authorize".equals(uri)) { +// // 你可以自定义条件,比如只对b-client强制加prompt=login +// if (request.getParameter("prompt") == null) { +// // 构造新的URL,加上prompt=login +// StringBuilder newUrl = new StringBuilder(request.getRequestURL()); +// newUrl.append("?"); +// request.getParameterMap().forEach((key, values) -> { +// for (String value : values) { +// if (!"prompt".equals(key)) { +// newUrl.append(URLEncoder.encode(key, StandardCharsets.UTF_8)).append("=") +// .append(URLEncoder.encode(value, StandardCharsets.UTF_8)).append("&"); +// } +// } +// }); +// newUrl.append("prompt=login"); +// response.sendRedirect(newUrl.toString()); +// return; // 必须return,防止后续Filter继续处理 +// } +// } + filterChain.doFilter(request, response); + } +} \ No newline at end of file diff --git a/oidc/src/main/java/com/tuoheng/oauth/oidc/provider/TenantAwareAuthenticationProvider.java b/oidc/src/main/java/com/tuoheng/oauth/oidc/provider/TenantAwareAuthenticationProvider.java index bb6b9b2..4ebb173 100644 --- a/oidc/src/main/java/com/tuoheng/oauth/oidc/provider/TenantAwareAuthenticationProvider.java +++ b/oidc/src/main/java/com/tuoheng/oauth/oidc/provider/TenantAwareAuthenticationProvider.java @@ -15,6 +15,9 @@ import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import java.util.HashMap; +import java.util.Map; + @Component public class TenantAwareAuthenticationProvider implements AuthenticationProvider { @@ -42,13 +45,20 @@ public class TenantAwareAuthenticationProvider implements AuthenticationProvider System.out.println("客户端ID: " + clientId); System.out.println("租户代码: " + tenantCode); - // 执行标准的用户认证 - UserDetails userDetails = userDetailsService.loadUserByUsername(username); + //在这边判断用户是否有权限 + UserDetails userDetails = userDetailsService.loadUserByUsername(username,clientId,tenantCode); + if (userDetails != null && passwordEncoder.matches(password, userDetails.getPassword())) { System.out.println("用户认证成功"); - return new UsernamePasswordAuthenticationToken( + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( userDetails, password, userDetails.getAuthorities()); + + Map details = new HashMap<>(); + details.put("client_id", clientId); + details.put("tenant_code", tenantCode); + token.setDetails(details); + return token; } System.out.println("用户认证失败"); diff --git a/oidc/src/main/java/com/tuoheng/oauth/oidc/service/CustomUserDetailsService.java b/oidc/src/main/java/com/tuoheng/oauth/oidc/service/CustomUserDetailsService.java index fe02ffb..74d8b30 100644 --- a/oidc/src/main/java/com/tuoheng/oauth/oidc/service/CustomUserDetailsService.java +++ b/oidc/src/main/java/com/tuoheng/oauth/oidc/service/CustomUserDetailsService.java @@ -16,13 +16,19 @@ public class CustomUserDetailsService implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; - @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - // 创建UserDetails对象 + /** + * 这个地方需要扩展,判断用户是否有权限 + * @param username + * @param clientId + * @param tenantCode + * @return + * @throws UsernameNotFoundException + */ + public UserDetails loadUserByUsername(String username,String clientId,String tenantCode) throws UsernameNotFoundException { return org.springframework.security.core.userdetails.User.builder() - .username("user") - .password(passwordEncoder.encode("password")) + .username("1") + .password(passwordEncoder.encode("1")) .authorities(new HashSet<>()) .accountExpired(false) .accountLocked(false) @@ -32,4 +38,9 @@ public class CustomUserDetailsService implements UserDetailsService { } + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + return null; + } + } diff --git a/oidc/src/main/java/com/tuoheng/oauth/oidc/token/CustomTokenCustomizer.java b/oidc/src/main/java/com/tuoheng/oauth/oidc/token/CustomTokenCustomizer.java new file mode 100644 index 0000000..4b78594 --- /dev/null +++ b/oidc/src/main/java/com/tuoheng/oauth/oidc/token/CustomTokenCustomizer.java @@ -0,0 +1,35 @@ +package com.tuoheng.oauth.oidc.token; + + + +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer; +import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Component +public class CustomTokenCustomizer implements OAuth2TokenCustomizer { + @Override + public void customize(JwtEncodingContext context) { + // 只对access_token生效 + if ("access_token".equals(context.getTokenType().getValue())) { + String username = context.getPrincipal().getName(); + context.getClaims().claim("username", username); + + // 取details + Object detailsObj = context.getPrincipal().getDetails(); + if (detailsObj instanceof Map details) { + Object clientId = details.get("client_id"); + Object tenantCode = details.get("tenant_code"); + if (clientId != null) { + context.getClaims().claim("client_id", clientId.toString()); + } + if (tenantCode != null) { + context.getClaims().claim("tenant_code", tenantCode.toString()); + } + } + } + } +} \ No newline at end of file