Go语言 ReactJS发送GET请求,响应302重定向,收到CORS错误

laximzn5  于 2024-01-04  发布在  Go
关注(0)|答案(4)|浏览(174)

这是我的stack:
1.前端服务器:Node + ReactJS(f.com
1.后端服务器(API):Golang(b.com
我在开发环境中使用create-react-app通过npm start命令运行我的前端服务器。我想使用在sso.example.com/login服务的SSO执行身份验证。理想情况下,每当有一个请求到后端服务器没有适当的授权,它将响应HTTP 302重定向到SSO页面。所以顺序是:

  1. ReactJS向b.com/users发送GET请求
  2. Golang回复了http.Redirect(“sso.example.com/login“)
  3. ReactJS应用程序从sso.example.com获取CORS错误,其中我无法控制添加Access-Control-Allow-Origin:*响应头
    如何成功重定向GET请求?
mhd8tkvw

mhd8tkvw1#

在你的情况下,在客户端的浏览器中进行重定向会更容易,所以你不会有CORS问题(因为你是直接从SSO网站URL访问它)。
你可以用react来做,这很复杂:Programmatically navigate using react router V4
或者用window和普通的JavaScript来做,这很容易,但可能会导致浏览历史记录的问题:

window.location.href = "https://www.example.com";

字符串
https://appendto.com/2016/04/javascript-redirect-how-to-redirect-a-web-page-with-javascript/

x0fgdtte

x0fgdtte2#

React发送的GET http://b.com/users请求是XMLHttpRequest还是fetch请求?与导航请求不同,这些请求不会发生在浏览上下文中。(JavaScript程序可以在对这样的请求的响应中接收HTML页面,但是它不对该HTML做任何事情。)因此,这些请求不能用SSO登录流来认证,因为像SAML或OpenID Connect这样的SSO协议依赖于浏览上下文,浏览器会在浏览上下文中呈现带有响应的HTML页面。(如果用户还没有登录,他们必须在HTML页面中输入他们的凭据。)
如果您的React应用依赖于对b.com的请求,那么您应该在应用开始向该网站发出任何请求之前 * 对该网站执行SSO登录流程。您可以显示一个按钮“登录b.com“,该按钮将在b.com处打开一个弹出窗口。在此弹出窗口中,然后浏览器被重定向到sso.example.com/login,(也许在用户给出他们的凭证之后)返回到b.com。此时,用户登录到b.com,可以再次关闭弹出窗口。(也有自动关闭这种登录弹出窗口的机制。)
所有这些都将在React应用程序向b.com发出任何fetch请求之前发生,并且由于它发生在浏览上下文中,因此根本不会涉及CORS。
(See(Access to fetch at https://accounts.google.com/o/oauth2/v2/auth has been blocked by CORS

r1wp621o

r1wp621o3#

您有:

Frontend Server (f.com)
   |
   |---[ReactJS App]
   |       |
   |       |---[GET Request]---> Backend Server (b.com)
   |                                     |
   |                                     |--[302 Redirect to SSO]---> SSO Server (sso.example.com)
   |
   └---[CORS Error]

字符串
我认为CORS error的出现是因为SSO服务器(sso.example.com)没有在响应中包含必要的CORS头。由于您无法控制SSO服务器,因此需要在设置中实现一个解决方案。
与其从Golang后端直接重定向到SSO服务器,不如在b.com上设置一个代理端点来处理重定向。该代理将在内部管理302重定向。
当对b.com/users的请求缺乏适当的授权时,后端应该重定向到b.com上的代理端点,而不是直接重定向到SSO服务器。
然后,b.com上的代理端点可以执行到sso.example.com/login的内部服务器端重定向。

Frontend Server (f.com)
   |
   |---[ReactJS App]
   |       |
   |       |---[GET Request]---> Backend Server (b.com)
   |                                     |
   |                                     |--[Redirect]---> Proxy Endpoint (b.com/proxy)
   |                                                    |
   |                                                    |---[Server-Side Redirect]---> SSO Server (sso.example.com)
   |
   └---[Authentication Flow Continues Without CORS Error]


Golang后端将包括

http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
    // Check for authorization
    if !isAuthorized(r) {
        // Redirect to the proxy endpoint
        http.Redirect(w, r, "https://b.com/proxy", http.StatusFound)
        return
    }
    // Handle authorized request
})

// Proxy endpoint handler
http.HandleFunc("/proxy", func(w http.ResponseWriter, r *http.Request) {
    // Perform server-side redirection to SSO server
    // Implement the server-side HTTP client logic here
})


对于代理服务器,您可以使用现有的反向代理工具或在Golang应用程序中编写自定义逻辑。代理将需要向sso.example.com/login执行HTTP请求并将响应中继回客户端。
这样,您就可以避免CORS问题,因为重定向是在服务器端处理的,客户端不直接与sso.example.com交互。

kx1ctssn

kx1ctssn4#

@paradite answer完全涵盖了你的问题,我想补充的是,在前端有React库为你处理这个问题:https://www.npmjs.com/package/@react-oauth/googlehttps://www.npmjs.com/package/react-social-login-buttons
它允许您使用一个预先制作的按钮,就像(在第一个包的情况下)

<GoogleLogin
  onSuccess={(resp) => { .. call your backend with resp .. })}
  onError={(e) => { ... })}
/>

字符串
这真的取决于你的用例;在npm包网站上有几个例子,我最近在我的一个项目中使用了它,可以说它简化了过程。
在后端,您需要决定要使用哪种流:用途:登录(更简单)或授权(如果您需要额外的作用域、访问令牌、刷新令牌等)
第一个更简单,因为你得到了JWT,你解码和验证,你有用户信息。
如果你想实现后者,让你的后端从客户端获取code,然后你的后端必须与提供者交换它,类似于这样:

type UserData struct {
    Email string
    Name  string
    Token *oauth2.Token
}

var googleOauthConfig = &oauth2.Config{
    RedirectURL:  "http://localhost:1323/oauth/callback",
    ClientID:     "your-client-id.apps.googleusercontent.com",
    ClientSecret: "your-secret",
    Scopes:       []string{"profile", "email"},
    Endpoint:     google.Endpoint,
}

func GetUserDataFromGoogle(code string) (UserData, error) {
    // Use code to get token and get user info from Google.
    var userData UserData
    token, err := googleOauthConfig.Exchange(context.Background(), code)
    if err != nil {
        return userData, fmt.Errorf("code exchange wrong: %s", err.Error())
    }
    response, err := http.Get("https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + token.AccessToken)
    if err != nil {
        return userData, fmt.Errorf("failed getting user info: %s", err.Error())
    }
    defer response.Body.Close()

    contents, err := io.ReadAll(response.Body)
    if err != nil {
        return userData, fmt.Errorf("failed read response: %s", err.Error())
    }
    err = json.Unmarshal(contents, &userData)
    userData.Token = token
    return userData, nil
}


令牌包括access_tokenrefresh_token,您可以使用它们从提供程序获取用户信息。
注意我在这里使用https://github.com/golang/oauth2库来实现协议。在你的处理程序中,你提取代码并调用这个函数,如果代码是有效的,你会得到userData,否则你会得到一个错误。
这绕过了CORS问题,并简化了解决方案。我在示例中使用了Google,但Oauth2库支持许多其他提供商。

相关问题