From e9f4832c3f1f323d0402c4fbdf72151e8da6f181 Mon Sep 17 00:00:00 2001 From: zlt2000 Date: Wed, 10 Jun 2020 23:18:10 +0800 Subject: [PATCH] fix #I1JQO6 --- ...ntUsernamePasswordAuthenticationToken.java | 37 ++++++++++++ .../sso/demo/controller/ApiController.java | 16 +++++ .../src/main/resources/static/callback.html | 1 + .../src/main/resources/static/index.html | 4 ++ .../central/oauth/config/SecurityConfig.java | 41 +++++++++++-- .../oauth/tenant/OauthAuthorizeAspect.java | 59 +++++++++++++++++++ .../tenant/TenantAuthenticationProvider.java | 32 ++++++++++ .../TenantAuthenticationSecurityConfig.java | 35 +++++++++++ ...tUsernamePasswordAuthenticationFilter.java | 59 +++++++++++++++++++ zlt-uaa/src/main/resources/application.yml | 7 ++- 10 files changed, 285 insertions(+), 6 deletions(-) create mode 100644 zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/token/TenantUsernamePasswordAuthenticationToken.java create mode 100644 zlt-uaa/src/main/java/com/central/oauth/tenant/OauthAuthorizeAspect.java create mode 100644 zlt-uaa/src/main/java/com/central/oauth/tenant/TenantAuthenticationProvider.java create mode 100644 zlt-uaa/src/main/java/com/central/oauth/tenant/TenantAuthenticationSecurityConfig.java create mode 100644 zlt-uaa/src/main/java/com/central/oauth/tenant/TenantUsernamePasswordAuthenticationFilter.java diff --git a/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/token/TenantUsernamePasswordAuthenticationToken.java b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/token/TenantUsernamePasswordAuthenticationToken.java new file mode 100644 index 0000000..e9546d5 --- /dev/null +++ b/zlt-commons/zlt-auth-client-spring-boot-starter/src/main/java/com/central/oauth2/common/token/TenantUsernamePasswordAuthenticationToken.java @@ -0,0 +1,37 @@ +package com.central.oauth2.common.token; + +import lombok.Getter; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +/** + * 增加租户id,解决不同租户单点登录时角色没变化 + * + * @author zlt + * @date 2020/6/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class TenantUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken { + private static final long serialVersionUID = -5638287853803374687L; + + /** + * 租户id + */ + @Getter + private final String clientId; + + public TenantUsernamePasswordAuthenticationToken(Object principal, Object credentials, String clientId) { + super(principal, credentials); + this.clientId = clientId; + } + + public TenantUsernamePasswordAuthenticationToken(Object principal, Object credentials, + Collection authorities, String clientId) { + super(principal, credentials, authorities); + this.clientId = clientId; + } +} diff --git a/zlt-demo/sso-demo/web-sso/src/main/java/com/sso/demo/controller/ApiController.java b/zlt-demo/sso-demo/web-sso/src/main/java/com/sso/demo/controller/ApiController.java index 16a8e51..05d893a 100644 --- a/zlt-demo/sso-demo/web-sso/src/main/java/com/sso/demo/controller/ApiController.java +++ b/zlt-demo/sso-demo/web-sso/src/main/java/com/sso/demo/controller/ApiController.java @@ -1,5 +1,6 @@ package com.sso.demo.controller; +import cn.hutool.core.collection.CollectionUtil; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -14,7 +15,9 @@ import org.springframework.web.client.RestTemplate; import sun.misc.BASE64Encoder; import java.io.UnsupportedEncodingException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -48,10 +51,12 @@ public class ApiController { String accessToken = (String)tokenMap.get("access_token"); //获取用户信息 Map userMap = getUserInfo(accessToken); + List roles = getRoles(userMap); Map result = new HashMap(2); result.put("tokenInfo", tokenMap); result.put("userInfo", userMap); + result.put("roles", roles); return result; } @@ -86,4 +91,15 @@ public class ApiController { Map result = restTemplate.getForObject(userInfoUri+"?access_token="+accessToken, Map.class); return (Map)result.get("datas"); } + + private List getRoles(Map userMap) { + List> roles = (List>)userMap.get("roles"); + List result = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(roles)) { + roles.forEach(e -> { + result.add(e.get("code")); + }); + } + return result; + } } diff --git a/zlt-demo/sso-demo/web-sso/src/main/resources/static/callback.html b/zlt-demo/sso-demo/web-sso/src/main/resources/static/callback.html index d8db09b..fc2d27d 100644 --- a/zlt-demo/sso-demo/web-sso/src/main/resources/static/callback.html +++ b/zlt-demo/sso-demo/web-sso/src/main/resources/static/callback.html @@ -27,6 +27,7 @@ console.log(result); sessionStorage.setItem('access_token', result.tokenInfo.access_token); sessionStorage.setItem('username', result.userInfo.username); + sessionStorage.setItem('roles', result.roles); window.location = sessionStorage.getItem('visitUri'); }}); }; diff --git a/zlt-demo/sso-demo/web-sso/src/main/resources/static/index.html b/zlt-demo/sso-demo/web-sso/src/main/resources/static/index.html index aaea8c4..6400012 100644 --- a/zlt-demo/sso-demo/web-sso/src/main/resources/static/index.html +++ b/zlt-demo/sso-demo/web-sso/src/main/resources/static/index.html @@ -9,6 +9,7 @@

用户名:

+

权限:[]

应用id:

token:

@@ -23,8 +24,10 @@ let accessToken = sessionStorage.getItem('access_token'); if (accessToken) {//已登录 let username = sessionStorage.getItem('username'); + let roles = sessionStorage.getItem("roles"); $('#accessToken').html(accessToken); $('#userName').html(username); + $('#roles').html(roles); $('#clientId').html(clientId); } else {//未登录 sessionStorage.setItem("visitUri", window.location.href); @@ -36,6 +39,7 @@ let accessToken = sessionStorage.getItem('access_token'); sessionStorage.removeItem('access_token'); sessionStorage.removeItem('username'); + sessionStorage.removeItem("roles"); window.location = uaaUri+'remove/token?redirect_uri=http://127.0.0.1:8081/index.html&access_token='+accessToken; } diff --git a/zlt-uaa/src/main/java/com/central/oauth/config/SecurityConfig.java b/zlt-uaa/src/main/java/com/central/oauth/config/SecurityConfig.java index 874cdf5..662e386 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/config/SecurityConfig.java +++ b/zlt-uaa/src/main/java/com/central/oauth/config/SecurityConfig.java @@ -1,8 +1,11 @@ package com.central.oauth.config; import com.central.common.constant.SecurityConstants; +import com.central.common.properties.TenantProperties; import com.central.oauth.filter.LoginProcessSetTenantFilter; import com.central.oauth.handler.OauthLogoutSuccessHandler; +import com.central.oauth.tenant.TenantAuthenticationSecurityConfig; +import com.central.oauth.tenant.TenantUsernamePasswordAuthenticationFilter; import com.central.oauth.mobile.MobileAuthenticationSecurityConfig; import com.central.oauth.openid.OpenIdAuthenticationSecurityConfig; import com.central.common.config.DefaultPasswordConfig; @@ -58,6 +61,15 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MobileAuthenticationSecurityConfig mobileAuthenticationSecurityConfig; + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private TenantAuthenticationSecurityConfig tenantAuthenticationSecurityConfig; + + @Autowired + private TenantProperties tenantProperties; + /** * 这一步的配置是必不可少的,否则SpringBoot会自动配置一个AuthenticationManager,覆盖掉内存中的用户 * @return 认证管理对象 @@ -68,17 +80,21 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { return super.authenticationManagerBean(); } + @Bean + public TenantUsernamePasswordAuthenticationFilter tenantAuthenticationFilter(AuthenticationManager authenticationManager) { + TenantUsernamePasswordAuthenticationFilter filter = new TenantUsernamePasswordAuthenticationFilter(); + filter.setAuthenticationManager(authenticationManager); + filter.setFilterProcessesUrl(SecurityConstants.OAUTH_LOGIN_PRO_URL); + filter.setAuthenticationSuccessHandler(authenticationSuccessHandler); + return filter; + } + @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest() //授权服务器关闭basic认证 .permitAll() - .and() - .formLogin() - .loginPage(SecurityConstants.LOGIN_PAGE) - .loginProcessingUrl(SecurityConstants.OAUTH_LOGIN_PRO_URL) - .successHandler(authenticationSuccessHandler) .and() .logout() .logoutUrl(SecurityConstants.LOGOUT_URL) @@ -97,6 +113,21 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { // 解决不允许显示在iframe的问题 .headers().frameOptions().disable().cacheControl(); + if (tenantProperties.getEnable()) { + //解决不同租户单点登录时角色没变化 + http.formLogin() + .loginPage(SecurityConstants.LOGIN_PAGE) + .and() + .addFilterAt(tenantAuthenticationFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class) + .apply(tenantAuthenticationSecurityConfig); + } else { + http.formLogin() + .loginPage(SecurityConstants.LOGIN_PAGE) + .loginProcessingUrl(SecurityConstants.OAUTH_LOGIN_PRO_URL) + .successHandler(authenticationSuccessHandler); + } + + // 基于密码 等模式可以无session,不支持授权码模式 if (authenticationEntryPoint != null) { http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint); diff --git a/zlt-uaa/src/main/java/com/central/oauth/tenant/OauthAuthorizeAspect.java b/zlt-uaa/src/main/java/com/central/oauth/tenant/OauthAuthorizeAspect.java new file mode 100644 index 0000000..3bc7bd9 --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/tenant/OauthAuthorizeAspect.java @@ -0,0 +1,59 @@ +package com.central.oauth.tenant; + +import com.central.common.context.TenantContextHolder; +import com.central.common.feign.UserService; +import com.central.common.model.LoginAppUser; +import com.central.oauth2.common.token.TenantUsernamePasswordAuthenticationToken; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.oauth2.common.util.OAuth2Utils; +import org.springframework.stereotype.Component; + +import java.security.Principal; +import java.util.Map; + +/** + * /oauth/authorize拦截器 + * 解决不同租户单点登录时角色没变化 + * + * @author zlt + * @date 2020/6/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Slf4j +@Component +@Aspect +public class OauthAuthorizeAspect { + @Autowired + private UserService userService; + + @Around("execution(* org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.authorize(..))") + public Object doAroundMethod(ProceedingJoinPoint joinPoint) throws Throwable { + Object[] args = joinPoint.getArgs(); + Map parameters = (Map) args[1]; + Principal principal = (Principal) args[3]; + if (principal instanceof TenantUsernamePasswordAuthenticationToken) { + TenantUsernamePasswordAuthenticationToken tenantToken = (TenantUsernamePasswordAuthenticationToken)principal; + String clientId = tenantToken.getClientId(); + String requestClientId = parameters.get(OAuth2Utils.CLIENT_ID); + //判断是否不同租户单点登录 + if (!requestClientId.equals(clientId)) { + try { + TenantContextHolder.setTenant(requestClientId); + //重新查询对应该租户的角色等信息 + LoginAppUser user = userService.findByUsername(tenantToken.getName()); + tenantToken = new TenantUsernamePasswordAuthenticationToken(user, tenantToken.getCredentials(), user.getAuthorities(), requestClientId); + args[3] = tenantToken; + } finally { + TenantContextHolder.clear(); + } + } + } + return joinPoint.proceed(args); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantAuthenticationProvider.java b/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantAuthenticationProvider.java new file mode 100644 index 0000000..8dc7e4b --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantAuthenticationProvider.java @@ -0,0 +1,32 @@ +package com.central.oauth.tenant; + +import com.central.oauth2.common.token.TenantUsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; + +/** + * 增加租户id,解决不同租户单点登录时角色没变化 + * + * @author zlt + * @date 2020/6/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class TenantAuthenticationProvider extends DaoAuthenticationProvider { + @Override + protected Authentication createSuccessAuthentication(Object principal, + Authentication authentication, UserDetails user) { + TenantUsernamePasswordAuthenticationToken authenticationToken = (TenantUsernamePasswordAuthenticationToken) authentication; + TenantUsernamePasswordAuthenticationToken result = new TenantUsernamePasswordAuthenticationToken( + principal, authentication.getCredentials(), user.getAuthorities(), authenticationToken.getClientId()); + result.setDetails(authenticationToken.getDetails()); + return result; + } + + @Override + public boolean supports(Class authentication) { + return TenantUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantAuthenticationSecurityConfig.java b/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantAuthenticationSecurityConfig.java new file mode 100644 index 0000000..88b75d7 --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantAuthenticationSecurityConfig.java @@ -0,0 +1,35 @@ +package com.central.oauth.tenant; + +import com.central.oauth.service.ZltUserDetailsService; +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.stereotype.Component; + +/** + * @author zlt + * @date 2020/6/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Component +public class TenantAuthenticationSecurityConfig extends SecurityConfigurerAdapter { + private ZltUserDetailsService userDetailsService; + + private PasswordEncoder passwordEncoder; + + public TenantAuthenticationSecurityConfig(ZltUserDetailsService userDetailsService, PasswordEncoder passwordEncoder) { + this.userDetailsService = userDetailsService; + this.passwordEncoder = passwordEncoder; + } + + @Override + public void configure(HttpSecurity http) { + TenantAuthenticationProvider provider = new TenantAuthenticationProvider(); + provider.setUserDetailsService(userDetailsService); + provider.setPasswordEncoder(passwordEncoder); + http.authenticationProvider(provider); + } +} diff --git a/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantUsernamePasswordAuthenticationFilter.java b/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantUsernamePasswordAuthenticationFilter.java new file mode 100644 index 0000000..2b6779d --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/tenant/TenantUsernamePasswordAuthenticationFilter.java @@ -0,0 +1,59 @@ +package com.central.oauth.tenant; + +import com.central.common.context.TenantContextHolder; +import com.central.oauth2.common.token.TenantUsernamePasswordAuthenticationToken; +import lombok.Setter; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 替换 UsernamePasswordAuthenticationFilter 增加租户id + * + * @author zlt + * @date 2020/6/10 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +@Setter +public class TenantUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { + private boolean postOnly = true; + + public TenantUsernamePasswordAuthenticationFilter () { + super(); + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, + HttpServletResponse response) throws AuthenticationException { + if (postOnly && !request.getMethod().equals("POST")) { + throw new AuthenticationServiceException( + "Authentication method not supported: " + request.getMethod()); + } + + String username = obtainUsername(request); + String password = obtainPassword(request); + String clientId = TenantContextHolder.getTenant(); + + if (username == null) { + username = ""; + } + + if (password == null) { + password = ""; + } + + username = username.trim(); + + TenantUsernamePasswordAuthenticationToken authRequest = new TenantUsernamePasswordAuthenticationToken(username, password, clientId); + + setDetails(request, authRequest); + + return getAuthenticationManager().authenticate(authRequest); + } +} diff --git a/zlt-uaa/src/main/resources/application.yml b/zlt-uaa/src/main/resources/application.yml index 03611a0..dd7b100 100644 --- a/zlt-uaa/src/main/resources/application.yml +++ b/zlt-uaa/src/main/resources/application.yml @@ -27,4 +27,9 @@ zlt: security: code: # 忽略验证码的应用编号 - ignoreClientCode: app \ No newline at end of file + ignoreClientCode: app + #多租户配置 + tenant: + enable: true + ignoreTables: + - oauth_client_details \ No newline at end of file -- GitLab