在两个应用程序之间共享 SPRING_SECURITY_CONTEXT [英] Share SPRING_SECURITY_CONTEXT between two applications

查看:43
本文介绍了在两个应用程序之间共享 SPRING_SECURITY_CONTEXT的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个不同的 Spring Boot 应用程序,它们在 localhost 上的不同端口(8080、8081)和不同的配置(application.yml)上运行.这些应用程序使用 SSO 和 OAuth 2.0 从授权服务器获取授权令牌.我登录到我的第一个应用程序,获得授权,这里一切正常.现在我需要与第二个 Spring Boot 应用程序(在端口 8081 上)共享这些身份验证详细信息,以授权授权服务器中的第二个应用程序.谷歌搜索并找到 2 个方法:我可以尝试在两个应用程序之间共享 HttpSession(但我认为它是多余的)或 HttpSessionSecurityContextRepository 作为 SecurityContextRepository 似乎更多方便的.这里的问题是我无法做到这一点,而且我仍然不确定在 2 个应用之间共享安全上下文是否是个好主意.

I have two different Spring Boot Applications that run on localhost on different ports (8080, 8081) and different configs (application.yml). These apps use SSO with OAuth 2.0 to get authorization token from Authorization Server. I log in to my first application, get authorization and everything works great here. Now I need to share these authentication details with second Spring Boot App (on port 8081) to authorize second app in Authorization Server. Googled and found 2 aproaches: I can try to share HttpSession between two apps (but I think it's redundant) OR HttpSessionSecurityContextRepository as SecurityContextRepository which seems more convenient. The problem here is that I can't manage to do so and I'm still not sure that it's a good idea to share Security Context between 2 apps.

我现在尝试的:

  1. 通过 GET 请求中的标头从第一个应用程序共享授权令牌(根据授权服务器请求的规范定制),但它不起作用 - 第二个应用程序不接受请注意这个令牌.
  2. 将第一个应用的授权 cookie 共享给第二个应用,但它也不起作用.
  1. Share authorization token from first app via headers in GET request (custom-built in accordance with specification for requests for Authorization Server), but it didn't work - second app doesn't take in mind this token.
  2. Share authorized cookie from first app to second, but it didn't work, too.

我无法在第二个应用程序上通过授权服务器进行授权,因为它可能不是带有 @Controller 的 Spring Boot 应用程序,而是任何其他没有 HTML 表单的应用程序,所以我需要在第一个应用程序(带有 UI) 上授权,获取执行授权请求所需的所有数据并将其传递给第二个应用程序(第三个、第四个......),以便他们能够也做授权请求.

I can't do authorization through Authorization Server on second app because it may be not a Spring Boot App with @Controller but any other app without HTML forms, so I need to authorize on first app (with UI), get all the data which is needed to perform authorized requests and pass it to second app (third, fourth...) so they will be able to do authorized requests too.

提前致谢!

推荐答案

我假设您的授权/资源服务器是外部应用程序.您可以使用第一个应用程序成功登录,因此流程正在工作.您有两个自己的客户端应用程序client_id、client_secret 等参数.如果这些参数不同,那么授权/资源服务器将为第一个和第二个客户端应用程序返回不同的 bareer tokensessionid cookie.否则你需要在授权/资源服务器中对它们进行授权.我会在用户登录第一个应用程序时提供,然后在后台登录第二个应用程序.为了自动授权第二个应用程序,您可以尝试在成功第一个应用程序登录后使用自己的参数手动为第二个应用程序执行 oauth2 登录流程,并将 cookie 发送到您从 oauth2 登录获得的前端.

I presume that your authorization/resource server is external application.And you can login successfully with your first application so flow is working.You have two client application with own client_id, client_secret and etc. parameters.If these parameters are different then authorization/resource server will return different bareer token and sessionid cookie for first and second client application.Otherwise you need to authorize both of them in authorization/resource server. I would offer when user do login to first app then in background you do login also for second application. For automatically authorizing second application you can try to do oauth2 login flow manually for second application with own parameters when after successful first application login and send cookies to frontend which you got from oauth2 login.

对于手动 oauth2 登录,您可以尝试以下代码:

For manual oauth2 login you can try below code:

private Cookie oauth2Login(String username, String password, String clientId, String clientSecret) {
    try {
        String oauthHost = InetAddress.getByName(OAUTH_HOST).getHostAddress();
        HttpHeaders headers = new HttpHeaders();
        RestTemplate restTemplate = new RestTemplate();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();

        // Basic Auth
        String plainCreds = clientId + ":" + clientSecret;
        byte[] plainCredsBytes = plainCreds.getBytes();
        byte[] base64CredsBytes = org.apache.commons.net.util.Base64.encodeBase64(plainCredsBytes);
        String base64Creds = new String(base64CredsBytes);
        headers.add("Authorization", "Basic " + base64Creds);
        // form param
        map.add("username", username);
        map.add("password", password);
        map.add("grant_type", GRANT_TYPE);
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map,
                headers);
        // CALLING TOKEN URL
        OauthTokenRespone res = null;
        try {
            res = restTemplate.postForObject(OAUTH_HOST, request,
                    OauthTokenRespone.class);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        Optional<OauthTokenRespone> optRes = Optional.ofNullable(res);
        String accessToken = optRes.orElseGet(() -> new OauthTokenRespone("", "", "", "", "", ""))
                .getAccess_token();
        // CALLING RESOURCE
        headers.clear();
        map.clear();
        headers.setContentType(MediaType.APPLICATION_JSON);
        map.add("access_token", accessToken);
        request = new HttpEntity<MultiValueMap<String, String>>(map, headers);

        Cookie oauthCookie = null;
        if (accessToken.length() > 0) {
            HttpEntity<String> response = restTemplate.exchange(
                    OAUTH_RESOURCE_URL.replace(OAUTH_HOST, oauthHost) + "?access_token=" + accessToken,
                    HttpMethod.POST, request, String.class);
            String cookie = Optional.ofNullable(response.getHeaders().get("Set-Cookie"))
                    .orElseGet(() -> Arrays.asList(new String(""))).get(0);
            if (cookie.length() > 0) {
                String[] c = cookie.split(";")[0].split("=");
                oauthCookie = new Cookie(c[0], c[1]);
                oauthCookie.setHttpOnly(true);
            }
        }
        return Optional.ofNullable(oauthCookie).orElseGet(() -> new Cookie("Ops", ""));
    } catch (Throwable t) {
        return new Cookie("Ops", "");
    }
}

@JsonIgnoreProperties(ignoreUnknown = true)
public class OauthTokenRespone {
    private String access_token;
    private String token_type;
    private String refresh_token;
    private String expires_in;
    private String scope;
    private String organization;
    // getter and setter
  }

并在第一次应用登录后调用此方法,如下所示:

And call this method after first app login as follows :

Cookie oauthCookie = oauth2Login(authenticationRequest.getUsername(), authenticationRequest.getPassword(),
            CLIENT_ID, CLIENT_SECRET);

获取 cookie 后,您需要更改其名称(例如 JSESSIONID-SECOND),因为相同的 cookie 会相互覆盖,并且还需要将其域路径更改为第二个应用程序域.

After getting cookie you need change its name (for example JSESSIONID-SECOND) because same cookies will override each other and also need to change its domain path to second app domain.

response.addCookie(oauthCookie);

最后你需要在响应中添加 cookie(它是 HttpServletResponse 引用).

Last you need add cookie to response (it is HttpServletResponse reference).

希望有帮助!

这篇关于在两个应用程序之间共享 SPRING_SECURITY_CONTEXT的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆