【无授权模式】单点登录 go-view

目录

一、需求背景

二、解决方案

三、实现步骤

1、ruoyi-vue-pro通用页面

2、ruoyi-vue-pro后台管理后端逻辑实现src/main/java/com/more/doubleu/module/system/controller/admin/auth/AuthController.java

[3、go-view 路由守卫实现](#3、go-view 路由守卫实现)

[4、go-view 接口api实现](#4、go-view 接口api实现)


一、需求背景

搞来搞去,这个单点登录也是快搞疯了。

前期已经搞了3种方案了,客户还是不满意,感觉不够直接------需要有登陆过第三方应用或者授权。没有达到:

一次登录,多系统可用!

一次登录,多系统可用!

一次登录,多系统可用!

也就是常常听说的在任意一个系统中登陆一次,如果访问其他有权限的系统就直接可用访问有权限的内容,无需授权,无需再登陆。

-====== 相关文章 =======

ruoyi-vue-pro数据大屏------纯前端单点登录

ruoyi-vue-pro数据大屏优化------使用yudao-moudle-sso优化单点登录

ruoyi-vue-pro优化------让菜单支持多个参数,一键直达【经营分析】、【生产报表】

二、解决方案

本方案以ruoyi-vue-pro后台管理应用 单点登录go-vue数据大屏为例。

1)ruoyi-vue-pro后台管理登陆成功后;

2)访问其他页面时,则将自己的的accessToken作为参数附加到url后,

例如: https://192.168.1.222:3000/big/?sso=${accessToken}

3)go-view数据大屏在路由守卫中对参数sso进行特别处理:

a)优先刷新本地可能留存的accessToken。

b)刷新失败,则通过accessToken完成一次登陆:

通过accessToken找到登陆过的user,完成自动登陆,返回新的accessToken。

c)go-view前端得到新的accessToken后,完成登陆后续过程,如获取用户菜单、权限等。

三、实现步骤

1、ruoyi-vue-pro通用页面

ruoyi-vue-pro后台管理应用参考前面的单点登陆文章配置好菜单,新建或修改 src\views\report\goview\bigscreen.vue,内容如下:

html 复制代码
<template>
  <div class="">
    <i-frame v-if="url" :src="url" referrerpolicy="no-referrer"/>
  </div>
</template>
<script>
import iFrame from "@/components/iFrame/index";
import * as auth from "@/utils/auth";
export default {
  name: "BigScreen",
  components: { iFrame },
  props: {
    /* 数据大屏的id */
    value: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      url: undefined,
    };
  },
  mounted(){
    var reportId = this.$route.params.id || this.$route.query.id || this.value

    if(reportId){
      this.url =  this.setting.reportServer + "chart/preview/"+reportId+"?sso=" + auth.getAccessToken()+"&tenantId="+auth.getTenantId();
    } else {
      this.url = this.setting.reportServer + "?sso=" + auth.getAccessToken()+"&tenantId="+auth.getTenantId();
    }
  }
};
</script>

2、ruoyi-vue-pro后台管理后端逻辑实现src/main/java/com/more/doubleu/module/system/controller/admin/auth/AuthController.java

java 复制代码
    @PostMapping("/system/auth/sso-login/{t}")
    @PermitAll
    @Operation(summary = "访问令牌")
    @Parameter(name = "t", description = "访问令牌", required = true, example = "")
    @OperateLog(enable = false)
    public CommonResult<AuthLoginRespVO> ssoLogin(@PathVariable("t") String token) {
        AuthLoginRespVO authLoginRespVO = authService.ssoLogin(token);
        return success(authLoginRespVO);
    }

src/main/java/com/more/doubleu/module/system/service/auth/AdminAuthServiceImpl.java

java 复制代码
    @Override
    public AuthLoginRespVO ssoLogin(String accessToken){
        OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.checkAccessToken(accessToken);
        AdminUserDO user = userService.getUser(accessTokenDO.getUserId());
        if(user==null) {
            throw new ServiceException(USER_NOT_EXISTS.getMsg());
        }

        // 创建 Token 令牌,记录登录日志
        AuthLoginRespVO tokenAfterLoginSuccess = createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
        return tokenAfterLoginSuccess;
    }

3、go-view 路由守卫实现

src\router\router-guards.ts

javascript 复制代码
router.beforeEach(async (to, from, next) => {
        
    // console.log(window['$t']('http.error_message'))
    const Loading = window['$loading'];
    Loading && Loading.start();
    const isErrorPage = router.getRoutes().findIndex((item) => item.name === to.name);
    if (isErrorPage === -1) {
      next({ name: PageEnum.ERROR_PAGE_NAME_404 })
    }
    var query = to.query;
    if(query && query.sso){
      var tenantId = query.tenantId || App.setting.tenantId;
      const info = getLocalStorage(StorageEnum.GO_SYSTEM_STORE)
      const oldTenantId = info && info[SystemStoreEnum.TENANT_INFO] ? info[SystemStoreEnum.TENANT_INFO]['tenantId'] : undefined
      // 清空本地存储
      if(oldTenantId && oldTenantId!=tenantId) {
        clearLocalStorage(StorageEnum.GO_SYSTEM_STORE);
        clearLocalStorage(StorageEnum.GO_REFRESH_TOKEN);
      }

      let systemStore = useSystemStore();
      systemStore.setItem(SystemStoreEnum.TENANT_INFO, {
        tenantId: tenantId
      })
      // @ts-ignore
      if (!routerAllowList.includes(to.name) && !loginCheck()) {
        var refreshToken = await refreshTokenApi();
        console.log("refreshToken 1", refreshToken)
        if(refreshToken && refreshToken.code===0) {
          await storeUserInfo(refreshToken);
        } else {
          var accessToken = await loginByAccessTokenApi(to.query.sso);
          if(accessToken && accessToken.code===0) {
            await storeUserInfo(accessToken);
          }
        }
      }
    }
    // @ts-ignore
    if (!routerAllowList.includes(to.name) && !loginCheck()) {
      next({ name: PageEnum.BASE_LOGIN_NAME })
    }
    next()
  })

4、go-view 接口api实现

src\api\path\system.api.ts

javascript 复制代码
export const refreshTokenApi = async () => {
  try {
    var refreshToken = getLocalStorage(StorageEnum.GO_REFRESH_TOKEN).refreshToken;
    if(refreshToken) {
      const res = await http(RequestHttpEnum.POST)(`${ModuleTypeEnum.SYSTEM}/auth/refresh-token?refreshToken=` + refreshToken)
      return res
    }
  } catch (err) {
    clearLocalStorage(StorageEnum.GO_REFRESH_TOKEN);
  }
  return null;
}

// * 登录
export const loginByAccessTokenApi = async (accessToken: any) => {
  try {
    const res = await http(RequestHttpEnum.POST)<AuthLoginRespVO>(`${ModuleTypeEnum.SYSTEM}/auth/sso-login/${accessToken}`, {})
    console.log(res)
    return res
  } catch (err) {
    console.log(err)
  }
  return null;
}

export const storeUserInfo = async (loginRes:any) => {
  let systemStore = useSystemStore();
  if(loginRes && loginRes.data) {
    // ① Token 信息(先存储下,保证可以加载个人信息)
    setLocalStorage(StorageEnum.GO_REFRESH_TOKEN, loginRes.data);
    const tokenValue = loginRes.data.accessToken
    const tokenName = 'Authorization'
    
    systemStore.setItem(SystemStoreEnum.USER_INFO, {
      [SystemStoreUserInfoEnum.USER_TOKEN]: tokenValue,
      [SystemStoreUserInfoEnum.TOKEN_NAME]: tokenName
    })

    // 个人信息
    const profileRes:any = await getUserPermissionApi();
    var user = profileRes.data.user;
    const id = user.id
    const username = user.username
    const nickname = user.nickname
    // 存储到 pinia
    systemStore.setItem(SystemStoreEnum.USER_INFO, {
      [SystemStoreUserInfoEnum.USER_TOKEN]: tokenValue,
      [SystemStoreUserInfoEnum.TOKEN_NAME]: tokenName,
      [SystemStoreUserInfoEnum.USER_ID]: id,
      [SystemStoreUserInfoEnum.USER_NAME]: username,
      [SystemStoreUserInfoEnum.NICK_NAME]: nickname,
    });
    
  }