Oauth2单点登录流程实现

oauth2-demo

介绍

这是一个基于spring cloud oauth2和vue实现的前后端分离的单点登录示例。

最后附上示例的代码实现。

后端服务

auth-server 认证服务 ,对应域名:auth.szile.com
auth-client 资源服务,对应域名:client.szile.com

角色

浏览器:客户端角色(也可以将两个域名理解为客户端角色)
auth-client:资源服务角色
auth-server: 认证服务角色

认证流程如下图:
在这里插入图片描述

详细介绍

首先,在client.szile.com域名下访问页面,请求一个需要登录才能访问的接口/data,资源服务判断当前未登录,返回自定义的code: 40101,表示当前未登录需要进行登录。
前端项目拦截该40101,请求/toLogin接口。

前端拦截代码:

if (data && data.code && data.code === '40101') {
      return Promise.resolve(data).then(() => {
        const modeUrl = router.mode === 'hash' ? "/#" : ''
        const redirectUrl = process.env.VUE_APP_PUBLIC_PATH + modeUrl + router.currentRoute.path
        /** 
         * /toLogin是Client配置的security.oauth2.sso.loginPath的值
         * redirect是配置SsoSecurityConfigurer.targetUrlParameter的值, <strong>这里是登录后能回到原来页面的关键</strong>。
        */
        service({
          url: '/toLogin',
          method: 'get',
          // header的Accept不能包含application/json
          headers: { 'Accept': 'text/html,application/xhtml+xml,application/xml,text/plain,*/*' },
          params: { redirect: redirectUrl }
        })
      })
    }

为什么请求/toLogin?是根据security.oauth2.sso.login-path配置的。

security:
  oauth2:
    sso:
      # 设置登录的路径
      login-path: /toLogin

请求/toLogin接口返回status:302 Location:http://oauth.szile.com/oauth/authorize?client_id=auth-client&redirect_uri=http://client.szile.com/toLogin?redirect%3D%252Fview%252F%2523%252Fauthclientpage&response_type=code&scope=read&state=7sM9yf。浏览器会根据响应请求http://oauth.szile.com/oauth/authorize接口,携带参数,请求认证服务进行授权,授权方式为code, 授权码方式。
在这里插入图片描述
浏览器重定向请求http://oauth.szile.com/oauth/authorize接口,认证服务接收到请求,判断当前是未登录状态,返回自定义code: 40155555 和 loginPageUrl参数,loginPageUrl=/view/#/authserverlogin。
前端拦截code: 40155555。获取loginPageUrl参数,将用户引导到登录页。

前端拦截代码:

if (data && data.code && data.code === "40155555") {
      window.open(data.data.loginPageUrl, '_self')
      return Promise.resolve(data)
    } 

在这里插入图片描述
在这里插入图片描述
用户输入用户名和密码调用/doLogin接口进行登录。登录成功后,/doLogin接口返回status:302 Location:http://oauth.szile.com/oauth/authorize?client_id=auth-client&redirect_uri=http://client.szile.com/toLogin?redirect%3D%252Fview%252F%2523%252Fauthclientpage&response_type=code&scope=read&state=7sM9yf。

在这里插入图片描述

浏览器接收到302,请求http://oauth.szile.com/oauth/authorize?client_id=auth-client&redirect_uri=http://client.szile.com/toLogin?redirect%3D%252Fview%252F%2523%252Fauthclientpage&response_type=code&scope=read&state=7sM9yf,即再次请求认证服务进行授权。

认证服务接收到/oauth/authorize请求,由于我配置的是自动授权,所以这里没有用户授权页面,返回status:302 Location:http://client.szile.com/toLogin?redirect=%2Fview%2F%23%2Fauthclientpage&code=ts7ATv&state=7sM9yf 。 可以看到返回了code=ts7ATv,这个code就是授权码。
在这里插入图片描述
浏览器接收到302,请求http://client.szile.com/toLogin?redirect=%2Fview%2F%23%2Fauthclientpage&code=ts7ATv&state=7sM9yf ,这次请求/toLogin接口不一样的是携带了授权码信息。
在这里插入图片描述
资源服务接收到/toLogin接口和携带的授权码信息,然后携带请求任务服务的/oauth/token接口请求token,获取到token后,向响应/toLogin接口并携带了targetUrl参数,targetUrl=http://client.szile.com/view/#/authclientpage。
需要知道的是这个响应信息会被浏览器认为是/doLogin的响应,因为是/doLogin 302重定向 到/oauth/authorize 然后有重定向到/toLogin。所以前端在/doLogin接口的响应处拦截targetUrl参数,重新回到http://client.szile.com/view/#/authclientpage 页面。这正是最初发起请求的页面。

doLogin(this.loginForm)
        .then((res) => {
          location.href = res.data.targetUrl;
        })
        .catch((err) => {
          console.log(err);
        });

示例代码:szile/oauth2-demo

Logo

前往低代码交流专区

更多推荐