認證和SSO(二)-OAuth2四種授權模式及項目改造為授權碼模式實現單點登陸SSO


1、OAuth2四種模式

1.1、密碼模式

  這也是我們之前一直使用的模式,流程如下;這種模式下,用戶敏感信息直接泄漏給了客戶端應用,因此這種模式只能用於客戶端應用是我們自己開發的。因此密碼模式一般用於自己開發的App或單頁面應用。

1.2、授權碼模式

  授權碼模式是四種模式中最繁瑣也是最安全的一種模式。用戶向客戶端發起請求時,客戶端應用引導用戶去授權服務器進行認證(需要有客戶端id和回調地址),認證成功授權服務器會將授權碼發送給客戶端應用,客戶端應用再通過授權碼,客戶端id,客戶端密碼去授權服務器換取令牌,授權服務器驗證無誤后,將令牌發送給客戶端應用。這種場景下,用戶的敏感信息沒有暴漏給客戶端應用,保證了安全。一般適用與客戶端應用是Web服務器或第三方的App。

1.3、簡化模式(隱式授權模式)

簡化模式相對於授權碼模式省略了,通過授權碼換取令牌過程。一般用於沒有服務器的前端應用。

4、客戶端模式

最簡單的模式,發出的令牌與用戶無關。因此,一般適用於我們完全信任的客戶端應用(服務器端服務),並且不需要用戶參與。

2、改造項目為授權碼認證方式

目前我們使用的是OAuth2的密碼模式,我們來修改為使用授權碼模式。

2.1、修改客戶端應用,需要用戶進行認證時直接引導去認證服務器完成認證

  2.1.1、修改index.html用戶未登錄時,直接跳轉到認證服務器進行獲取授權碼,需提供客戶端id(client_id)、回調地址(redirect_uri)、返回值類型(response_type)為code、狀態標記(state)傳什么返回什么,為可選項。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>cfq security</title>
</head>

<body>
<h1>Study Security</h1>

<div id="login-success">
    <h1>登陸成功!</h1>

    <p>
        <button onclick="getOrder()">獲取訂單信息</button>
    </p>

    <table>
        <tr>
            <td>order id</td>
            <td><input id="orderId"/></td>
        </tr>
        <tr>
            <td>order product id</td>
            <td><input id="productId"/></td>
        </tr>
    </table>

    </hr>
    <p>
        <button onclick="logout()">退出</button>
    </p>

</div>

<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
    //判斷用戶是否登陸
    $(function () {
        $.get("/me", function (data) {
            if (data) {
                //已登錄
                $("#login-success").show();
                $("#goto-login").hide();
            } else {
                //未登錄
                $("#login-success").hide();
                //$("#goto-login").show();
                location.href = "http://auth.caofanqi.cn:9020/oauth/authorize?" +
                    "client_id=webApp&" +
                    "redirect_uri=http://web.caofanqi.cn:9000/oauth/callback&" +
                    "response_type=code&" +
                    "state=abc";
            }
        });
    });

    //登陸方法
    function login() {
        var username = $("#username").val();
        var password = $("#password").val();

        $.ajax({
            type: "POST",
            url: "/login",
            contentType: "application/json;charset=utf-8",
            data: JSON.stringify({"username": username, "password": password}),
            success: function (msg) {
                location.href = "/";
            },
            error: function (msg) {
                alert("登錄失敗!!")
            }
        });
    }

    //獲取訂單信息,通過/api轉發到網關,通過/order轉發到order微服務
    function getOrder() {
        $.get("/api/order/orders/1", function (data) {
            $("#orderId").val(data.id);
            $("#productId").val(data.productId);
        });
    }

    //退出
    function logout() {
        $.get("/logout",function(){});
        location.href = "/";
    }

</script>
</body>
</html>

  2.1.2、提供回調方法

    /**
     * 回調方法
     *  接收認證服務器發來的授權碼,並換取令牌
     * @param code 授權碼
     * @param state 請求授權服務器時發送的state
     */
    @GetMapping("/oauth/callback")
    public void oauthCallback(@RequestParam String code, String state, HttpServletRequest request, HttpServletResponse response) throws IOException {

        String oauthTokenUrl = "http://gateway.caofanqi.cn:9010/token/oauth/token";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.setBasicAuth("webApp", "123456");

        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.set("code",code);
        params.set("grant_type", "authorization_code");
        params.set("redirect_uri", "http://web.caofanqi.cn:9000/oauth/callback");

        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(params, headers);

        ResponseEntity<TokenInfoDTO> authResult = restTemplate.exchange(oauthTokenUrl, HttpMethod.POST, httpEntity, TokenInfoDTO.class);

        request.getSession().setAttribute("token", authResult.getBody());
        log.info("tokenInfo : {}",authResult.getBody());

        log.info("state :{}",state);
        //一般會根據state記錄需要登陸時的路由
        response.sendRedirect("/");
    }

2.2、認證服務器配置支持授權碼模式

2.3、啟動各服務,進行測試

  2.3.1、訪問http://web.caofanqi.cn:9000/  會直接跳轉到認證服務器

  2.3.3、輸入zhangsan,123456進行登陸,會進行權限選擇

如果想跨過權限選擇,可以設置autoapprove為true

  2.3.4、點擊Authorize會調轉會我們設定的頁面,再WebApp服務的日志中可以看到我們傳的state給原封不動的返回來了

   2.3.5、點擊獲取訂單信息,可以正常獲得

2.3、到目前為止,我們實現了由OAuth2密碼模式替換為OAuth2授權碼模式,同時也實現了SSO,微服務環境下前后端分離的單點登陸。為什么說實現了SSO呢,我們可以把再啟動一個webApp服務,並把涉及到的端口改為8090,客戶端表中加入一條記錄,各修改如下

    

 訪問http://web.caofanqi.cn:8090/ ,並沒有讓我們進行登陸,直接就是登陸狀態

  這是為什么呢?因為在這種模式下,登陸的位置是認證服務器,只要在認證服務器的session沒過期,客戶端在任何時候跳過去,認證服務器就知道你是誰,就不會讓你輸入用戶名和密碼了,直接跳回客戶端應用去,如果之前的令牌還有效,就直接發給客戶端應用,無效了,就會新生成一個發給客戶端應用。

  但是這種方式還有一些問題,我們在后面進行探討。

 

項目源碼:https://github.com/caofanqi/study-security/tree/dev-web-authorization_code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM