This commit is contained in:
parent
a856062a90
commit
f91d65803e
|
|
@ -5,6 +5,7 @@ import com.nimbusds.jose.jwk.RSAKey;
|
||||||
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
|
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
|
||||||
import com.nimbusds.jose.jwk.source.JWKSource;
|
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||||
import com.nimbusds.jose.proc.SecurityContext;
|
import com.nimbusds.jose.proc.SecurityContext;
|
||||||
|
import com.tuoheng.oauth.oidc.filter.ForcePromptLoginFilter;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
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.Customizer;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
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.User;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
|
@ -47,12 +50,24 @@ import java.security.interfaces.RSAPrivateKey;
|
||||||
import java.security.interfaces.RSAPublicKey;
|
import java.security.interfaces.RSAPublicKey;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FilterRegistrationBean<ForcePromptLoginFilter> forcePromptLoginFilterRegistration(ForcePromptLoginFilter filter) {
|
||||||
|
FilterRegistrationBean<ForcePromptLoginFilter> registration = new FilterRegistrationBean<>();
|
||||||
|
registration.setFilter(filter);
|
||||||
|
registration.addUrlPatterns("/oauth2/authorize");
|
||||||
|
registration.setOrder(-101); // 顺序要比Spring Security的Filter更靠前
|
||||||
|
return registration;
|
||||||
|
}
|
||||||
@Autowired
|
@Autowired
|
||||||
@Lazy
|
@Lazy
|
||||||
AuthenticationProvider tenantAwareAuthenticationProvider;
|
AuthenticationProvider tenantAwareAuthenticationProvider;
|
||||||
|
|
@ -62,10 +77,11 @@ public class SecurityConfig {
|
||||||
super(loginFormUrl);
|
super(loginFormUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response, org.springframework.security.core.AuthenticationException exception) {
|
protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response, org.springframework.security.core.AuthenticationException exception) {
|
||||||
String loginUrl = super.determineUrlToUseForThisRequest(request, response, exception);
|
String loginUrl = super.determineUrlToUseForThisRequest(request, response, exception);
|
||||||
|
|
||||||
// 获取client_id参数
|
// 获取client_id参数
|
||||||
String clientId = request.getParameter("client_id");
|
String clientId = request.getParameter("client_id");
|
||||||
if (clientId != null && !clientId.isEmpty()) {
|
if (clientId != null && !clientId.isEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,9 @@ import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class TenantAwareAuthenticationProvider implements AuthenticationProvider {
|
public class TenantAwareAuthenticationProvider implements AuthenticationProvider {
|
||||||
|
|
||||||
|
|
@ -42,13 +45,20 @@ public class TenantAwareAuthenticationProvider implements AuthenticationProvider
|
||||||
System.out.println("客户端ID: " + clientId);
|
System.out.println("客户端ID: " + clientId);
|
||||||
System.out.println("租户代码: " + tenantCode);
|
System.out.println("租户代码: " + tenantCode);
|
||||||
|
|
||||||
// 执行标准的用户认证
|
//在这边判断用户是否有权限
|
||||||
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
UserDetails userDetails = userDetailsService.loadUserByUsername(username,clientId,tenantCode);
|
||||||
|
|
||||||
|
|
||||||
if (userDetails != null && passwordEncoder.matches(password, userDetails.getPassword())) {
|
if (userDetails != null && passwordEncoder.matches(password, userDetails.getPassword())) {
|
||||||
System.out.println("用户认证成功");
|
System.out.println("用户认证成功");
|
||||||
return new UsernamePasswordAuthenticationToken(
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
|
||||||
userDetails, password, userDetails.getAuthorities());
|
userDetails, password, userDetails.getAuthorities());
|
||||||
|
|
||||||
|
Map<String, Object> details = new HashMap<>();
|
||||||
|
details.put("client_id", clientId);
|
||||||
|
details.put("tenant_code", tenantCode);
|
||||||
|
token.setDetails(details);
|
||||||
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("用户认证失败");
|
System.out.println("用户认证失败");
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,19 @@ public class CustomUserDetailsService implements UserDetailsService {
|
||||||
@Autowired
|
@Autowired
|
||||||
private PasswordEncoder passwordEncoder;
|
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()
|
return org.springframework.security.core.userdetails.User.builder()
|
||||||
.username("user")
|
.username("1")
|
||||||
.password(passwordEncoder.encode("password"))
|
.password(passwordEncoder.encode("1"))
|
||||||
.authorities(new HashSet<>())
|
.authorities(new HashSet<>())
|
||||||
.accountExpired(false)
|
.accountExpired(false)
|
||||||
.accountLocked(false)
|
.accountLocked(false)
|
||||||
|
|
@ -32,4 +38,9 @@ public class CustomUserDetailsService implements UserDetailsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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<JwtEncodingContext> {
|
||||||
|
@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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue