fix: SSO登录退出相关问题

This commit is contained in:
CaptainB 2022-10-20 18:35:36 +08:00 committed by 刘瑞斌
parent 7b0284f38f
commit 14c2d2dd81
7 changed files with 94 additions and 28 deletions

View File

@ -9,6 +9,7 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.reactive.result.view.Rendering; import org.springframework.web.reactive.result.view.Rendering;
import org.springframework.web.server.WebSession; import org.springframework.web.server.WebSession;
import reactor.core.publisher.Mono;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Locale; import java.util.Locale;
@ -23,7 +24,7 @@ public class SSOController {
@MsAuditLog(module = OperLogModule.AUTH_TITLE, type = OperLogConstants.LOGIN, title = "登录") @MsAuditLog(module = OperLogModule.AUTH_TITLE, type = OperLogConstants.LOGIN, title = "登录")
public Rendering callbackWithAuthId(@RequestParam("code") String code, @PathVariable("authId") String authId, WebSession session, Locale locale) throws Exception { public Rendering callbackWithAuthId(@RequestParam("code") String code, @PathVariable("authId") String authId, WebSession session, Locale locale) throws Exception {
ssoService.exchangeToken(code, authId, session, locale); ssoService.exchangeToken(code, authId, session, locale);
return Rendering.redirectTo("/?_token=" + CodingUtil.base64Encoding(session.getId())) return Rendering.redirectTo("/#/?_token=" + CodingUtil.base64Encoding(session.getId()))
.build(); .build();
} }
@ -31,7 +32,7 @@ public class SSOController {
@MsAuditLog(module = OperLogModule.AUTH_TITLE, type = OperLogConstants.LOGIN, title = "登录") @MsAuditLog(module = OperLogModule.AUTH_TITLE, type = OperLogConstants.LOGIN, title = "登录")
public Rendering callback(@RequestParam("code") String code, @RequestParam("state") String authId, WebSession session, Locale locale) throws Exception { public Rendering callback(@RequestParam("code") String code, @RequestParam("state") String authId, WebSession session, Locale locale) throws Exception {
ssoService.exchangeToken(code, authId, session, locale); ssoService.exchangeToken(code, authId, session, locale);
return Rendering.redirectTo("/?_token=" + CodingUtil.base64Encoding(session.getId())) return Rendering.redirectTo("/#/?_token=" + CodingUtil.base64Encoding(session.getId()))
.build(); .build();
} }
@ -39,28 +40,26 @@ public class SSOController {
@MsAuditLog(module = OperLogModule.AUTH_TITLE, type = OperLogConstants.LOGIN, title = "登录") @MsAuditLog(module = OperLogModule.AUTH_TITLE, type = OperLogConstants.LOGIN, title = "登录")
public Rendering casCallback(@RequestParam("ticket") String ticket, @PathVariable("authId") String authId, WebSession session, Locale locale) throws Exception { public Rendering casCallback(@RequestParam("ticket") String ticket, @PathVariable("authId") String authId, WebSession session, Locale locale) throws Exception {
ssoService.serviceValidate(ticket, authId, session, locale); ssoService.serviceValidate(ticket, authId, session, locale);
return Rendering.redirectTo("/?_token=" + CodingUtil.base64Encoding(session.getId())) return Rendering.redirectTo("/#/?_token=" + CodingUtil.base64Encoding(session.getId()))
.build(); .build();
} }
/** // /**
* oidc 登出 callback // * oidc 登出 callback
*/ // */
@PostMapping("/callback/logout") // @PostMapping("/callback/logout")
public Rendering logoutCallback(@RequestParam("logout_token") String logoutToken) { // public Mono<Void> logoutCallback(@RequestParam("logout_token") String logoutToken) {
ssoService.kickOutUser(logoutToken); // ssoService.kickOutUser(logoutToken);
return Rendering.redirectTo("/#/login") // return Mono.empty();
.build(); // }
} //
// /**
/** // * cas 登出 callback
* cas 登出 callback // */
*/ // @PostMapping("/callback/cas/logout")
@PostMapping("/callback/cas/logout") // public Mono<Void> logoutCasCallback(@RequestParam("logoutRequest") String logoutRequest) {
public Rendering logoutCasCallback(@RequestParam("logoutRequest") String logoutRequest) { // ssoService.kickOutCasUser(logoutRequest);
ssoService.kickOutCasUser(logoutRequest); // return Mono.empty();
return Rendering.redirectTo("/#/login") // }
.build();
}
} }

View File

@ -18,7 +18,7 @@ import java.util.Optional;
public class SessionFilter implements WebFilter { public class SessionFilter implements WebFilter {
// 所有模块的前缀 // 所有模块的前缀
private static final String[] PREFIX = new String[]{"/setting", "/project", "/api", "/performance", "/track", "/workstation", "/ui", "/report"}; private static final String[] PREFIX = new String[]{"/setting", "/project", "/api", "/performance", "/track", "/workstation", "/ui", "/report"};
private static final String[] TO_SUB_SERVICE = new String[]{"/license", "/system", "/resource"}; private static final String[] TO_SUB_SERVICE = new String[]{"/license", "/system", "/resource", "/sso/callback/logout", "/sso/callback/cas/logout"};
private static final String PERFORMANCE_DOWNLOAD_PREFIX = "/jmeter/"; private static final String PERFORMANCE_DOWNLOAD_PREFIX = "/jmeter/";
private static final String API_DOWNLOAD_PREFIX = "/api/jmeter/"; private static final String API_DOWNLOAD_PREFIX = "/api/jmeter/";

View File

@ -189,6 +189,7 @@ export default {
activated() { activated() {
this.initTableData(); this.initTableData();
this.$router.replace({query: null})
}, },
methods: { methods: {
currentUser: () => { currentUser: () => {

View File

@ -1,5 +1,6 @@
import Vue from 'vue'; import Vue from 'vue';
import VueI18n from "vue-i18n"; import VueI18n from "vue-i18n";
Vue.use(VueI18n); Vue.use(VueI18n);
// 直接加载翻译的语言文件 // 直接加载翻译的语言文件
@ -45,9 +46,10 @@ const setLang = lang => {
} }
export const setLanguage = lang => { export const setLanguage = lang => {
if (lang) { if (!lang) {
lang = lang.replace('_', '-'); return;
} }
lang = lang.replace('_', '-');
if (i18n.locale !== lang) { if (i18n.locale !== lang) {
importLanguage(lang).then(setLang); importLanguage(lang).then(setLang);
} else { } else {

View File

@ -39,9 +39,8 @@ instance.interceptors.request.use(
} }
// sso callback 过来的会有sessionId在url上 // sso callback 过来的会有sessionId在url上
if (!config.headers['X-AUTH-TOKEN']) { if (!config.headers['X-AUTH-TOKEN']) {
const paramsStr = window.location.search const paramsStr = window.location.href
const params = new URLSearchParams(paramsStr) let sessionId = paramsStr.split('_token=')[1]
let sessionId = params.get('_token');
if (sessionId) { if (sessionId) {
config.headers['X-AUTH-TOKEN'] = Base64.decode(sessionId); config.headers['X-AUTH-TOKEN'] = Base64.decode(sessionId);
} }

View File

@ -0,0 +1,33 @@
package io.metersphere.controller;
import io.metersphere.service.SSOLogoutService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("sso")
public class SSOLogoutController {
@Resource
private SSOLogoutService ssoLogoutService;
/**
* oidc 登出 callback
*/
@PostMapping("/callback/logout")
public void logoutCallback(@RequestParam("logout_token") String logoutToken) {
ssoLogoutService.kickOutUser(logoutToken);
}
/**
* cas 登出 callback
*/
@PostMapping("/callback/cas/logout")
public void logoutCasCallback(@RequestParam("logoutRequest") String logoutRequest) {
ssoLogoutService.kickOutCasUser(logoutRequest);
}
}

View File

@ -3,9 +3,12 @@ package io.metersphere.service;
import io.metersphere.base.domain.AuthSource; import io.metersphere.base.domain.AuthSource;
import io.metersphere.base.mapper.AuthSourceMapper; import io.metersphere.base.mapper.AuthSourceMapper;
import io.metersphere.commons.constants.UserSource; import io.metersphere.commons.constants.UserSource;
import io.metersphere.commons.utils.CodingUtil;
import io.metersphere.commons.utils.JSON; import io.metersphere.commons.utils.JSON;
import io.metersphere.commons.utils.SessionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
@ -18,6 +21,8 @@ public class SSOLogoutService {
private AuthSourceMapper authSourceMapper; private AuthSourceMapper authSourceMapper;
@Resource @Resource
private RestTemplate restTemplate; private RestTemplate restTemplate;
@Resource
private StringRedisTemplate stringRedisTemplate;
/** /**
* oidc logout * oidc logout
@ -35,4 +40,31 @@ public class SSOLogoutService {
} }
} }
} }
public void kickOutUser(String logoutToken) {
String[] split = StringUtils.split(logoutToken, '.');
for (String s : split) {
String v = CodingUtil.base64Decoding(s);
Map obj = JSON.parseMap(v);
String sub = (String) obj.get("sub");
if (StringUtils.isNotEmpty(sub)) {
SessionUtils.kickOutUser(sub);
break;
}
}
}
public void kickOutCasUser(String logoutRequest) {
String ticket = StringUtils.substringBetween(logoutRequest, "<samlp:SessionIndex>", "</samlp:SessionIndex>");
if (StringUtils.isEmpty(ticket)) {
return;
}
String name = stringRedisTemplate.opsForValue().get(ticket);
if (StringUtils.isEmpty(name)) {
return;
}
SessionUtils.kickOutUser(name);
stringRedisTemplate.delete(ticket);
}
} }