1. 程式人生 > >樂優商城(三十一)——授權中心

樂優商城(三十一)——授權中心

目錄

三、首頁判斷登入狀態

3.1 頁面程式碼

3.2 後臺實現校驗使用者介面

3.3 測試

3.4 重新整理token

四、閘道器的登入攔截

4.1 引入jwt相關配置

4.2 編寫過濾邏輯

4.3 白名單


三、首頁判斷登入狀態

雖然cookie已經成功寫入,但是首頁的頂部,登入狀態依然沒能判斷出使用者資訊:

這裡需要向後臺發起請求,根據cookie獲取當前使用者的資訊。

3.1 頁面程式碼

頁面的頂部已經封裝為一個獨立的Vue元件,在/js/pages/shortcut.js

在created函式中,查詢使用者資訊:

後臺:

請求已經發出,因為token在cookie中,因此本次請求肯定會攜帶token資訊在頭中。

3.2 後臺實現校驗使用者介面

leyou-authentication-service中定義使用者的校驗介面,通過cookie獲取token,然後校驗通過返回使用者資訊。

  • 請求方式:GET

  • 請求路徑:/verify

  • 請求引數:無,但需要從cookie中獲取token資訊

  • 返回結果:UserInfo,校驗成功返回使用者資訊;校驗失敗,則返回401

程式碼:

   /**
     * 使用者驗證
     * @param token
     * @return
     */
    @GetMapping("verify")
    public ResponseEntity<UserInfo> verifyUser(@CookieValue("LY_TOKEN") String token){
        try{
            //1.從token中解析token資訊
            UserInfo userInfo = JwtUtils.getInfoFromToken(token,this.properties.getPublicKey());
            //2.解析成功返回使用者資訊
            return ResponseEntity.ok(userInfo);
        }catch (Exception e){
            e.printStackTrace();
        }
        //3.出現異常,相應401
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
    }

getInfoFromToken程式碼:

    /**
     * 獲取token中的使用者資訊
     *
     * @param token     使用者請求中的令牌
     * @param publicKey 公鑰
     * @return 使用者資訊
     * @throws Exception
     */
    public static UserInfo getInfoFromToken(String token, PublicKey publicKey) throws Exception {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        return new UserInfo(
                ObjectUtils.toLong(body.get(JwtConstans.JWT_KEY_ID)),
                ObjectUtils.toString(body.get(JwtConstans.JWT_KEY_USER_NAME))
        );
    }

3.3 測試

頁面效果:

3.4 重新整理token

每當使用者在頁面進行新的操作,都應該重新整理token的過期時間,否則30分鐘後用戶的登入資訊就無效了。而重新整理其實就是重新生成一份token,然後寫入cookie即可。

那麼問題來了:怎麼知道使用者有操作呢?

事實上,每當使用者來查詢其個人資訊,就證明他正在瀏覽網頁,此時重新整理cookie是比較合適的時機。因此可以對校驗使用者登入狀態的介面進行改進,加入重新整理token的邏輯。

    /**
     * 使用者驗證
     * @param token
     * @return
     */
    @GetMapping("verify")
    public ResponseEntity<UserInfo> verifyUser(@CookieValue("LY_TOKEN") String token,HttpServletRequest request,
                                               HttpServletResponse response){
        try{
            //1.從token中解析token資訊
            UserInfo userInfo = JwtUtils.getInfoFromToken(token,this.properties.getPublicKey());
            //2.解析成功要重新重新整理token
            token = JwtUtils.generateToken(userInfo,this.properties.getPrivateKey(),this.properties.getExpire());
            //3.更新Cookie中的token
            CookieUtils.setCookie(request,response,this.properties.getCookieName(),token,this.properties.getCookieMaxAge());
            //4.解析成功返回使用者資訊
            return ResponseEntity.ok(userInfo);
        }catch (Exception e){
            e.printStackTrace();
        }
        //5.出現異常,相應401
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
    }

四、閘道器的登入攔截

在Zuul編寫攔截器,對使用者的token進行校驗,如果發現未登入,則進行攔截。

4.1 引入jwt相關配置

既然是登入攔截,一定是前置攔截器,在leyou-gateway中定義。

首先在pom.xml中,引入所需要的依賴:

<dependency>
    <groupId>com.leyou.common</groupId>
    <artifactId>leyou-common</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>com.leyou.auth</groupId>
    <artifactId>leyou-auth-common</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

然後編寫application.yml,新增如下內容:

leyou:
  jwt:
    pubKeyPath:  C:\\tmp\\rsa\\rsa.pub # 公鑰地址
    cookieName: LY_TOKEN # cookie的名稱

4.2 編寫過濾邏輯

基本邏輯:

  • 獲取cookie中的token

  • 通過JWT對token進行校驗

  • 通過:則放行;不通過:則重定向到登入頁

程式碼:

package com.leyou.filter;

import com.leyou.auth.utils.JwtUtils;
import com.leyou.config.JwtProperties;
import com.leyou.utils.CookieUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * @Author: 98050
 * @Time: 2018-10-24 16:21
 * @Feature: 登入攔截器
 */
@Component
@EnableConfigurationProperties(JwtProperties.class)
public class LoginFilter extends ZuulFilter {

    @Autowired
    private JwtProperties properties;

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 5;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        //1.獲取上下文
        RequestContext context = RequestContext.getCurrentContext();
        //2.獲取request
        HttpServletRequest request = context.getRequest();
        //3.獲取token
        String token = CookieUtils.getCookieValue(request,this.properties.getCookieName());
        //4.校驗
        try{
            //4.1 校驗通過,放行
            JwtUtils.getInfoFromToken(token,this.properties.getPublicKey());
        }catch (Exception e){
            //4.2 校驗不通過,返回403
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(HttpStatus.FORBIDDEN.value());
        }
        return null;
    }
}

重啟,重新整理頁面,發現請求校驗的介面也被攔截了:

證明攔截器生效了,但是,這個路徑是不應該被攔截,需要白名單。

4.3 白名單

要注意,並不是所有的路徑我們都需要攔截,例如:

  • 登入校驗介面:/auth/**

  • 註冊介面:/user/register

  • 資料校驗介面:/user/check/**

  • 傳送驗證碼介面:/user/code

  • 搜尋介面:/search/**

另外,跟後臺管理相關的介面,因為沒有做登入和許可權,因此暫時都放行,但是生產環境中要做登入校驗:

  • 後臺商品服務:/item/**

所以,需要在攔截時,配置一個白名單,如果在名單內,則不進行攔截。

application.yaml中新增規則:

leyou:
  filter:
    allowPaths:
      - /api/auth
      - /api/search
      - /api/user/register
      - /api/user/check
      - /api/user/code
      - /api/item

然後讀取這些屬性:

package com.leyou.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.List;

/**
 * @Author: 98050
 * @Time: 2018-10-24 16:55
 * @Feature: 過濾白名單
 */
@ConfigurationProperties(prefix = "leyou.filter")
public class FilterProperties {

    private List<String> allowPaths;

    public List<String> getAllowPaths() {
        return allowPaths;
    }

    public void setAllowPaths(List<String> allowPaths) {
        this.allowPaths = allowPaths;
    }
}

在過濾器中的shouldFilter方法中新增判斷邏輯:

    @Override
    public boolean shouldFilter() {
        //1.獲取上下文
        RequestContext context = RequestContext.getCurrentContext();
        //2.獲取request
        HttpServletRequest request = context.getRequest();
        //3.獲取路徑
        String requestUri = request.getRequestURI();
        logger.info(requestUri);
        //4.判斷白名單
        return !isAllowPath(requestUri);
    }

    private boolean isAllowPath(String requestUri) {
        //1.定義一個標記
        boolean flag = false;

        //2.遍歷允許訪問的路徑
        for (String path : this.filterProperties.getAllowPaths()){
            if (requestUri.startsWith(path)){
                flag = true;
                break;
            }
        }
        return flag;
    }

知識點:

request.getRequestURL() 返回全路徑

request.getRequestURI() 返回除去host(域名或者ip)部分的路徑

request.getContextPath() 返回工程名部分,如果工程對映為/,此處返回則為空

request.getServletPath() 返回除去host和工程名部分的路徑

例如:
request.getRequestURL() :http://localhost:8080/jqueryLearn/resources/request.jsp 
request.getRequestURI(): /jqueryLearn/resources/request.jsp
request.getContextPath():/jqueryLearn 
request.getServletPath():/resources/request.j

重新整理頁面:

五、退出

刪除cookie中的token即可。

logout() {
    // 刪除cookie中的token即可
    Cookies.remove("LY_TOKEN",{
        path:"/",
        domain:"leyou.com"
    });
    window.location = 'http://www.leyou.com'
}