1. 程式人生 > >淘淘商城系列——單點登入之使用者登入

淘淘商城系列——單點登入之使用者登入

我們先來看下使用者登入流程圖,如下圖所示。使用者登入涉及到三個部分,第一部分是淘淘商城前臺工程,第二部分是單點登入服務,第三部分是Redis服務。具體流程下圖已經說的很明白了,我就不再囉嗦一遍了,相比於傳統的登入,我們沒有把使用者登入資訊存在session當中,而是存放到了Redis資料庫當中。
這裡寫圖片描述
對於上面的使用者登入流程圖,我仍粗略作一下解釋,使用者登入的處理流程大致可分為以下幾個步驟:

  1. 使用者在登入頁面提交使用者名稱和密碼。
  2. 使用者登入成功後生成token,token就相當於原來的jsessionid,它是一個字串,可以使用uuid演算法來生成。
  3. 把使用者資訊儲存到Redis資料庫裡面,key就是token,value就是TbUser物件轉換成的json,Redis儲存資訊時建議使用String型別來儲存,並且可以使用“字首:token”作為key。
  4. 設定key的過期時間,相當於模擬Session的過期時間,一般置為半個小時。
  5. 把token寫入cookie中。
  6. cookie需要跨域,並且是跨多個二級域名,例如www.taotao.com、sso.taotao.com、order.taotao.com。
  7. 設定cookie的有效期,建議設定為關閉瀏覽器cookie即失效。
  8. 登入成功。

下面我們就來實現服務端的登入業務。
由於使用者登入只涉及到單表操作,因此我們使用逆向工程生成的dao層程式碼即可。
下面我們來編寫Service層的程式碼。首先在taotao-sso-interface工程的com.taotao.sso.service包下新建一個使用者登入的介面——UserLoginService.java,如下圖所示。
這裡寫圖片描述


緊接著在taotao-sso-service工程的com.taotao.sso.service.impl包中編寫UserLoginService介面的實現類——UserLoginServiceImpl.java,實現完之後,UserLoginServiceImpl類的所有程式碼如下:

/**
 * 使用者登入處理Service
 * <p>Title: UserLoginServiceImpl</p>
 * <p>Description: </p>
 * <p>Company: www.itcast.cn</p> 
 * @version
1.0 */
@Service public class UserLoginServiceImpl implements UserLoginService { @Autowired private TbUserMapper userMapper; @Autowired private JedisClient jedisClient; @Value("${SESSION_PRE}") private String SESSION_PRE; @Value("${SESSION_EXPIRE}") private Integer SESSION_EXPIRE; @Override public TaotaoResult login(String username, String password) { // 判斷使用者名稱和密碼是否正確 TbUserExample example = new TbUserExample(); Criteria criteria = example.createCriteria(); criteria.andUsernameEqualTo(username); List<TbUser> list = userMapper.selectByExample(example); if (list == null || list.size() == 0) { return TaotaoResult.build(400, "使用者名稱或密碼錯誤"); } // 校驗密碼,密碼要進行md5加密後再校驗 TbUser user = list.get(0); if (!DigestUtils.md5DigestAsHex(password.getBytes()).equals(user.getPassword())) { return TaotaoResult.build(400, "使用者名稱或密碼錯誤"); } // 生成一個token String token = UUID.randomUUID().toString(); // 把使用者資訊儲存到Redis資料庫裡面去 // key就是token,value就是使用者物件轉換成json user.setPassword(null); // 為了安全,就不要把密碼儲存到Redis資料庫裡面去,因為這樣太危險了,因此我們先把密碼置空 jedisClient.set(SESSION_PRE + ":" + token, JsonUtils.objectToJson(user)); // 設定key的過期時間 jedisClient.expire(SESSION_PRE + ":" + token, SESSION_EXPIRE); // 返回結果 return TaotaoResult.ok(token); } }

UserLoginServiceImpl類的程式碼中SESSION_PRE和SESSION_EXPIRE這兩個欄位的值配置在配置檔案中,如下圖所示。
這裡寫圖片描述
下面我們便釋出一下服務,如下圖所示。即在applicationContext-service.xml配置檔案中新增如下配置:

<dubbo:service interface="com.taotao.sso.service.UserLoginService" ref="userLoginServiceImpl" timeout="300000" />

這裡寫圖片描述
這樣我們的Service層程式碼便寫完了。
下面我們接著來編寫Controller層的程式碼。首先我們新增對dubbo服務的引用,即在taotao-sso-web工程下的springmvc.xml配置檔案中新增如下配置:

<dubbo:reference interface="com.taotao.sso.service.UserLoginService" id="userLoginService" />

這裡寫圖片描述
接著來看一下使用者登入的介面文件,如下圖所示。
這裡寫圖片描述
我們需要把token儲存到cookie當中,為了方便的操作cookie,我們特意封裝了一個工具類,由於該工具類有可能被多個服務使用,因此我們最好把該工具類放到taotao-common工程中,如下圖所示。
這裡寫圖片描述
可以看到當前導包是有錯誤的,要解決這個問題就要在taotao-common工程中新增對servlet-api的依賴,新增完依賴之後CookieUtils類就不報錯了,如下圖所示。
這裡寫圖片描述
為了方便大家複製,現將CookieUtils工具類的程式碼給出。

/**
 * 
 * Cookie 工具類
 *
 */
public final class CookieUtils {

    /**
     * 得到Cookie的值, 不編碼
     * 
     * @param request
     * @param cookieName
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName) {
        return getCookieValue(request, cookieName, false);
    }

    /**
     * 得到Cookie的值,
     * 
     * @param request
     * @param cookieName
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || cookieName == null) {
            return null;
        }
        String retValue = null;
        try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList[i].getName().equals(cookieName)) {
                    if (isDecoder) {
                        retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
                    } else {
                        retValue = cookieList[i].getValue();
                    }
                    break;
                }
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return retValue;
    }

    /**
     * 得到Cookie的值,
     * 
     * @param request
     * @param cookieName
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || cookieName == null) {
            return null;
        }
        String retValue = null;
        try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList[i].getName().equals(cookieName)) {
                    retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
                    break;
                }
            }
        } catch (UnsupportedEncodingException e) {
             e.printStackTrace();
        }
        return retValue;
    }

    /**
     * 設定Cookie的值 不設定生效時間預設瀏覽器關閉即失效,也不編碼
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
            String cookieValue) {
        setCookie(request, response, cookieName, cookieValue, -1);
    }

    /**
     * 設定Cookie的值 在指定時間內生效,但不編碼
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
            String cookieValue, int cookieMaxage) {
        setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);
    }

    /**
     * 設定Cookie的值 不設定生效時間,但編碼
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
            String cookieValue, boolean isEncode) {
        setCookie(request, response, cookieName, cookieValue, -1, isEncode);
    }

    /**
     * 設定Cookie的值 在指定時間內生效, 編碼引數
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
            String cookieValue, int cookieMaxage, boolean isEncode) {
        doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);
    }

    /**
     * 設定Cookie的值 在指定時間內生效, 編碼引數(指定編碼)
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
            String cookieValue, int cookieMaxage, String encodeString) {
        doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
    }

    /**
     * 刪除Cookie帶cookie域名
     */
    public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,
            String cookieName) {
        doSetCookie(request, response, cookieName, "", -1, false);
    }

    /**
     * 設定Cookie的值,並使其在指定時間內生效
     * 
     * @param cookieMaxage cookie生效的最大秒數
     */
    private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
            String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
        try {
            if (cookieValue == null) {
                cookieValue = "";
            } else if (isEncode) {
                cookieValue = URLEncoder.encode(cookieValue, "utf-8");
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxage > 0)
                cookie.setMaxAge(cookieMaxage);
            if (null != request) {// 設定域名的cookie
                String domainName = getDomainName(request);
                System.out.println(domainName);
                if (!"localhost".equals(domainName)) {
                    cookie.setDomain(domainName);
                }
            }
            cookie.setPath("/");
            response.addCookie(cookie);
        } catch (Exception e) {
             e.printStackTrace();
        }
    }

    /**
     * 設定Cookie的值,並使其在指定時間內生效
     * 
     * @param cookieMaxage cookie生效的最大秒數
     */
    private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
            String cookieName, String cookieValue, int cookieMaxage, String encodeString) {
        try {
            if (cookieValue == null) {
                cookieValue = "";
            } else {
                cookieValue = URLEncoder.encode(cookieValue, encodeString);
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxage > 0)
                cookie.setMaxAge(cookieMaxage);
            if (null != request) {// 設定域名的cookie
                String domainName = getDomainName(request);
                System.out.println(domainName);
                if (!"localhost".equals(domainName)) {
                    cookie.setDomain(domainName);
                }
            }
            cookie.setPath("/");
            response.addCookie(cookie);
        } catch (Exception e) {
             e.printStackTrace();
        }
    }

    /**
     * 得到cookie的域名
     */
    private static final String getDomainName(HttpServletRequest request) {
        String domainName = null;

        String serverName = request.getRequestURL().toString();
        if (serverName == null || serverName.equals("")) {
            domainName = "";
        } else {
            serverName = serverName.toLowerCase();
            serverName = serverName.substring(7);
            final int end = serverName.indexOf("/");
            serverName = serverName.substring(0, end);
            final String[] domains = serverName.split("\\.");
            int len = domains.length;
            if (len > 3) {
                // www.xxx.com.cn
                domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
            } else if (len <= 3 && len > 1) {
                // xxx.com or xxx.cn
                domainName = "." + domains[len - 2] + "." + domains[len - 1];
            } else {
                domainName = serverName;
            }
        }

        if (domainName != null && domainName.indexOf(":") > 0) {
            String[] ary = domainName.split("\\:");
            domainName = ary[0];
        }
        return domainName;
    }

}

接下來我們要在taotao-sso-web工程的UserController類中新增如下圖所示標註的程式碼。
這裡寫圖片描述
為方便大家複製,現將UserController類的程式碼貼出。

/**
 * 使用者管理Controller
 * <p>Title: UserController</p>
 * <p>Description: </p>
 * <p>Company: www.itcast.cn</p> 
 * @version 1.0
 */
@Controller
public class UserController {

    @Autowired
    private UserRegisterService userRegisterService;

    @Autowired
    private UserLoginService userLoginService;

    @Value("${COOKIE_TOKEN_KEY}")
    private String COOKIE_TOKEN_KEY;

    @RequestMapping(value="/user/check/{param}/{type}", method=RequestMethod.GET)
    @ResponseBody
    public TaotaoResult checkUserInfo(@PathVariable String param, @PathVariable Integer type) {
        TaotaoResult result = userRegisterService.checkUserInfo(param, type);
        return result;
    }

    @RequestMapping(value="/user/register", method=RequestMethod.POST)
    @ResponseBody
    public TaotaoResult registerUser(TbUser user) {
        TaotaoResult taotaoResult = userRegisterService.createUser(user);
        return taotaoResult;
    }

    @RequestMapping(value="/user/login", method=RequestMethod.POST)
    @ResponseBody
    public TaotaoResult userLogin(String username, String password,
            HttpServletRequest request, HttpServletResponse response) {
        TaotaoResult taotaoResult = userLoginService.login(username, password);
        // 取出token
        String token = taotaoResult.getData().toString();
        // 在返回結果之前,設定cookie(即將token寫入cookie)
        // 1.cookie怎麼跨域?
        // 2.如何設定cookie的有效期?
        CookieUtils.setCookie(request, response, COOKIE_TOKEN_KEY, token);
        // 返回結果
        return taotaoResult;
    }
}

UserController類的程式碼中COOKIE_TOKEN_KEY這個欄位的值配置在resource.properties配置檔案中,如下圖所示。
這裡寫圖片描述
下面我們來進行測試,由於在taotao-common中添加了一個工具類,因此需要重新打包taotao-common工程到本地maven倉庫,又由於在taotao-sso-interface工程中新添加了一個介面,因此我們還需要把taotao-sso工程重新打包到本地maven倉庫。注意:在啟動工程前要保證zookeeper和Redis伺服器處於啟動狀態!!! Redis伺服器的IP及埠要與applicationContext-redis.xml配置檔案一致,如下圖所示。
這裡寫圖片描述
以上準備工作做好之後,我們啟動taotao-sso工程和taotao-sso-web工程,啟動成功之後,我們還是使用RESTClient3.5來測試,最關鍵的兩步我已截圖。登入我們只用使用者名稱和密碼,不用電話和郵箱,所以新增完使用者名稱和密碼這兩個引數之後,點選”Generate”,如下圖所示。
這裡寫圖片描述
點選執行按鈕,可以看到返回的結果狀態碼是200,data中存放的是token的值。說明登入成功了,同時也說明我們的登入介面正確。
這裡寫圖片描述
登入成功了,我們利用redis-desktop-manager工具來檢視token是否已經被儲存起來了,如下圖所示,發現已經被儲存起來了。
這裡寫圖片描述

相關推薦

商城系列——登入使用者登入

我們先來看下使用者登入流程圖,如下圖所示。使用者登入涉及到三個部分,第一部分是淘淘商城前臺工程,第二部分是單點登入服務,第三部分是Redis服務。具體流程下圖已經說的很明白了,我就不再囉嗦一遍了,相比於傳統的登入,我們沒有把使用者登入資訊存在session當中,

商城系列——登入使用者註冊

首先我們還是看關於單點登入介面文件中關於使用者註冊介面的說明,如下圖所示。請求方式是POST型別,URL為我們明確指定好了,引數有四個,需要校驗完合法性之後再提交。 以上介面文件寫的還算明瞭,大家就湊合著看吧! 由於使用者註冊只涉及到單表操作,因此我們使

商城系列——登入使用者登入頁面實現

我們來看下login.jsp頁面,如下圖所示。可以看到<input .../>標籤中並沒有定義onclick方法,只是定義了個id="loginsubmit",那麼onclick事件在哪兒定義了呢? 其實是在js程式碼當中定義了,如下圖所示,頁

商城——登入使用者登入

我們先來看下使用者登入流程圖,如下圖所示。使用者登入涉及到三個部分,第一部分是淘淘商城前臺工程,第二部分是單點登入服務,第三部分是Redis服務。具體流程下圖已經說的很明白了,我就不再囉嗦一遍了,相比於傳統的登入,我們沒有把使用者登入資訊存在session當中,而是存放到了Re

商城26_登入SSO_02程式碼的實現,雪崩演算法

一、驗證資料是否可用 1. 介面文件 2. 需求分析 URL:請求的路徑 引數:param   type    param:username/phone/email     type:1/2/3 返回型別

商城26_登入SSO_01專案的搭建及配置

一、什麼是單點登入 1. 我們要做的單點登入 taotao-portal-web  taotao-search-web   taotao-item-web  這個三個專案,都得用到使用者,  三個tomcat 意味要有三臺伺服器

商城系列——SSM框架整合逆向工程

我們知道在開發中有些工作是非常耗時但是又沒有什麼技術含量的,比如建立mapper檔案、pojo、dao等,我們更關心的是業務邏輯的處理,而不是這些無關緊要的東西,因此如果能有工具幫我們自動生成程式碼將是非常棒的一件事情,這些mybatis官方已經為我們考慮到了,mybatis官方提供了逆向生成程式

(十一)Java B2B2C o2o多使用者商城-SSO登入OAuth2.0登入流程(2)

上一篇是站在巨人的肩膀上去研究OAuth2.0,也是為了快速幫助大家認識OAuth2.0,閒話少說,我根據框架中OAuth2.0的使用總結,畫了一個簡單的流程圖(根據使用者名稱+密碼實現OAuth2.0的登入認證):    上面的圖很清楚的描述了當前登入login的流程,現在我們針對於

(十一)Java B2B2C o2o多用戶商城-SSO登錄OAuth2.0登錄流程(2)

-- 設計思想 組件 expire image term 總結 pass 一起 上一篇是站在巨人的肩膀上去研究OAuth2.0,也是為了快速幫助大家認識OAuth2.0,閑話少說,我根據框架中OAuth2.0的使用總結,畫了一個簡單的流程圖(根據用戶名+密碼實現OAuth2

Scrapy框架模擬登入cookie登入

新建douban專案 通過charles  獲取到cookie後 貼上進入pycharm, 尼瑪格式混亂,無奈手工新增冒號,同時將=號換成:號。。。 cookies = { "bid": "EP3q1inffgg", "__yadk_uid" : "bI

第三方登入QQ登入(二)——OAuth2.0處理流程介紹(以QQ登入為例)

為了讓網站更快接入,騰訊提供了JS SDK的接入方案,具體點選這裡或者點選這裡進行檢視。不過也可以自己寫程式碼,當然你需要了解QQ的OAuth2.0的處理流程(Web Server Flow)。OAuth2.0的處理流程:點選這裡檢視官方Wiki。1.授權:獲取授權碼Auth

學習商城第八十九課(登入通過token獲取使用者資訊)

        首先還是看介面文件關於通過token獲取使用者資訊的說明,如下圖所示。             看了說明文件我們便知道該怎麼做了,在taotao-sso-interface工程的UserService介面類中新增一個介面,如下圖所示。          

商城61-SSO登入系統安全退出介面開發

目錄 1.分析介面文件 從介面文件可知 url:user/logout/{token} 引數:token 返回值:由TaotaoResule封裝的資訊"OK" 2.安全退出介面開發 2.1表現層 2.1.1dao層 直接使

商城登入安全退出

之前做淘淘商城時有個小問題是單點登入系統的安全退出問題,怎麼才能在點選 【退出】 時,跳轉到主頁面,也就是localhost:8082。其實很簡單,就是在寫controller時,返回一個跳轉頁面的字串,如下面的程式碼: @RequestMapping("/us

商城——登入使用者註冊

首先我們還是看關於單點登入介面文件中關於使用者註冊介面的說明,如下圖所示。請求方式是POST型別,URL為我們明確指定好了,引數有四個,需要校驗完合法性之後再提交。 以上介面文件寫的還算明瞭,大家就湊合著看吧! 由於使用者註冊只涉及到單表操作,因此我們使用逆向工程生成的dao層

帶你逐步深入瞭解SSM框架——商城專案登入系統實現

1.  課程計劃 1、  實現單點登入系統 2、  實現使用者的登入功能 3、  實現使用者的註冊功能 2.  單點登入系統分析 2.1. 什麼是SSO SSO英文全稱Single Sign On

商城59-SSO登入系統使用者登入介面開發

目錄 1.分析介面文件 請求的url:/user/login 請求的方法:POST 引數:username、password。 返回值:json資料,使用TaotaoResult包含一個token。 2.登入介面實現

day77_商城專案_10_ Linux下的Nginx代理詳解(配置虛擬主機+實現反向代理+實現負載均衡+高可用) + 登入系統工程搭建 + SSO系統介面文件講解_匠心筆記

淘淘商城專案_10 1、目前訪問系統使用的方式存在的問題 2、什麼是nginx 3、nginx的應用場景 4、nginx的安裝 4.1、要求的安裝環境 4.2、安裝步驟 4.3、啟動nginx

登入系統的介面開發——商城(二十四)

工程搭建 工程目錄。 pom.xml中tomcat埠配置為8084。 web.xml中配置工程的url字尾為 / 。 RedisDao與rest工程中的相同。 介面開發 在tao

登入系統架構——商城(二十三)

SSO 單點登入系統(Single Sign On, SSO):在多個應用系統中,使用者只需要登入一次就可以訪問所有相互信任的應用系統,是目前比較流行的企業業務整合的解決方案之一。