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.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<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
|
||||
@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()) {
|
||||
|
|
|
|||
|
|
@ -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.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<String, Object> details = new HashMap<>();
|
||||
details.put("client_id", clientId);
|
||||
details.put("tenant_code", tenantCode);
|
||||
token.setDetails(details);
|
||||
return token;
|
||||
}
|
||||
|
||||
System.out.println("用户认证失败");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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