提交 e9f4832c 编写于 作者: zlt2000's avatar zlt2000

fix #I1JQO6

上级 96032588
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
* <p>
* 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<? extends GrantedAuthority> authorities, String clientId) {
super(principal, credentials, authorities);
this.clientId = clientId;
}
}
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<String> 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<String> getRoles(Map userMap) {
List<Map<String, String>> roles = (List<Map<String, String>>)userMap.get("roles");
List<String> result = new ArrayList<>();
if (CollectionUtil.isNotEmpty(roles)) {
roles.forEach(e -> {
result.add(e.get("code"));
});
}
return result;
}
}
......@@ -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');
}});
};
......
......@@ -9,6 +9,7 @@
<body>
<div>
<p>用户名:<span id="userName"></span></p>
<p>权限:[<span id="roles"></span>]</p>
<p>应用id:<span id="clientId"></span></p>
<p>token:<span id="accessToken"></span></p>
<p><input type="button" value="登出" onclick="logout()"/></p>
......@@ -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;
}
</script>
......
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);
......
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
* <p>
* 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<String, String> parameters = (Map<String, String>) 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);
}
}
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
* <p>
* 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);
}
}
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
* <p>
* Blog: https://zlt2000.gitee.io
* Github: https://github.com/zlt2000
*/
@Component
public class TenantAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
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);
}
}
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
* <p>
* 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);
}
}
......@@ -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
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册