程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> 淺析博客園的保存密碼並自動登錄, 然後自己寫一個demo,淺析demo

淺析博客園的保存密碼並自動登錄, 然後自己寫一個demo,淺析demo

編輯:JAVA綜合教程

淺析博客園的保存密碼並自動登錄, 然後自己寫一個demo,淺析demo


  現在的網站基本上都有保存密碼並自動登錄的功能, 那麼密碼到底保存在哪裡呢? 你會發現, 同一個網站, 如果換一台電腦或者換一個浏覽器那就需要重新輸入用戶名和密碼, 從這裡可以看出, 密碼是保存在浏覽器的. 今天就來分析一下博客園的登錄並自己寫一個demo. 密碼是保存在浏覽器的cookie中的, 那什麼叫cookie呢? w3cshool中有這樣的定義: cookie 是存儲於訪問者的計算機中的變量。每當同一台計算機通過浏覽器請求某個頁面時,就會發送這個 cookie。是的, 也就是說, 只要浏覽器訪問服務器並且cookie是存在的(當然, 路徑有效才行 ), 浏覽器就會攜帶cookie到服務器. 這是一種自發的行為, 並不需要設置. 就好像你每天出門一定會帶手機一樣, 並不需要別人提醒你.

  好了, 進入正題. 看一下博客園是怎麼保存密碼的. 假如我沒有登錄過博客園, 打開到博客園的登錄界面, 然後再浏覽器中查看一下關於博客園的cookie, 可以看的這樣的:

  

  有3個cookie,分別是這樣的3個:   並沒有關於用戶名密碼的cookie, 嘿嘿, 因為我此時根本沒有登錄. 好了, 現在登錄

  

 

  開始我並沒有勾選下次自動登錄的選項, 登錄之後重定向到首頁. 在開發者工具的network中看一下

  

  可以看到, 多了一個.CNBlogsCookie的請求cookie, 咦咦咦, 剛才沒有的啊, 現在就有了, 於是我猜測這就是用戶名和密碼的cookie, 這只是暫時的一個猜測. 在內容設置中看一下關於這個cookie更詳細的信息

  

  剛才總共3個, 現在總共5個, 比剛才多了2個, 看一下多的2個

  

  另外一個是這樣的:

  

  可以看到, 2個cookie都是關閉浏覽器時過期, 現在關閉浏覽器? 不不不, 先看一下哪個保存著登錄信息, 先把SERVERID這個cookie刪除, 看一下是否還保持著登錄狀態. 試一下你會發現此時仍保持登錄狀態, 但是如果把.CNBlogsCookie這個cookie刪掉, 你會發現'掉線'了. 由此可以說明登錄信息保存在.CNBlogsCookie這個cookie中, 啊嘞, 我為什麼要說登錄信息呢? 剛才不是說的用戶名密碼麼? 登錄信息不就是指的用戶名和密碼嗎? 我覺得不一定要把密碼存到cookie中, 可以把一串和密碼相關的字符串存到cookie中, 在進行cookie自動登錄時, 在數據庫中查詢用戶名和這個字符串, 在表單提交時, 查詢用戶名和密碼, 個人覺得這樣更安全. 如果是把用戶名和密碼保存在了cookie中, 那麼就算銷毀session退出登錄, 下次登錄時, 在登錄頁面浏覽器應該自動填寫用戶名和密碼才對(畢竟cookie裡面有啊). 就博客園來說, 並不是這樣的, 可能你會反駁: "我明明退出登錄下次再登錄時用戶名和密碼就在那兒啊.", 其實這並不是cookie的功能, 而是浏覽器自己幫你做的, 現在的浏覽器一般都有個的功能, 好吧, 這個我想這也算個cookie, 不過此cookie非彼cookie, 呃, 有點混亂, 我也不確定這個功能算不算cookie. 總之, 關閉這個功能, 然會退出登錄再打開博客園你會發現登錄信息是沒有的, 所以通過分析我覺得cookie中不一定保存了密碼.

  那麼這個cookie是服務器什麼時候傳給浏覽器的, 上面看到的是request cookie, 那麼服務器是什麼時候response的, 登錄是ajax請求登錄的, 你可以用浏覽器打斷點看一下, 可以發現.CNBlogsCookie是登錄請求時response回來的, 如下圖

  

  好了, 現在也知道登錄信息保存在哪裡了, 現在關閉浏覽器再打開, 你會發現此時登錄狀態已經沒有了, 因為保存登錄信息的cookie已經消失了, 關閉浏覽器即代表結束一次會話(銷毀session). 嗯, 到這裡我們基本知道博客園是怎麼保存密碼的(可能並沒有把密碼保存在cookie中, 可能只是保存了一個與密碼相關的字符串, 也可能保存的是經過加密的密碼), 只是沒有將cookie保存在客戶端硬盤中而已(沒有勾選下次自動登錄), 那麼是怎麼實現自動登錄呢? 想想cookie的工作原理: 浏覽器每次請求服務器, 如果存在cookie, 並且域和路徑符合要求, 都會將cookie攜帶至服務器.(域和路徑看上面的圖) 所以只要訪問該網站, 服務器判斷cookie中的值,  然後判斷用戶是否可以改為登錄狀態就行了.

  所以登錄頁面勾選下次自動登錄就是表示將cookie存到硬盤中, 大概清楚了博客園保存密碼並自動登錄的方式: 首次登錄時將登錄信息保存到cookie中, 下次登錄時浏覽器攜帶包含登錄信息的cookie到服務器校驗從而顯示自動登錄

  自己寫一個保存密碼並自動登錄的demo, 兩個頁面loginUI.jsp和successUI.jsp, loginUI.jsp用於登錄, successUI.jsp只有成功登錄之後才能訪問

  loginUI.jsp:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>cookie login</title>
      <script type="text/javascript" src="js/jquery-1.8.3.min.js"></script>
    <script type="text/javascript">
        function checkCookie(){
            //把用戶名取出來
            var username = getCookie("loginInfo");
            $(":text").val(username);
        }
        
        function getCookie(c_name){
            if(document.cookie.length > 0){
                var coo = document.cookie;
                var start = coo.indexOf(c_name+"=")+c_name.length+1;
                var end = coo.indexOf(";", start);
                if(end == -1)
                    end = coo.length;
                var cookieValue = decodeURIComponent(coo.substring(start, end));
                return cookieValue.split(",")[0];
            }
        }
    </script>
  </head>
  
  <body onload="checkCookie()">
    <form action="cookie_login.action" method="post">
        用戶名<input type="text" name="user.username"><br>
        密碼<input type="password" name="user.password"><br>
        <input type="submit" value="登錄">
    </form>
  </body>
</html>
View Code

  這裡也涉及到了用js操作cookie    

 

  successUI.jsp

<body>
    登陸後才能看見我
  </body>
View Code

 

  後台是struts2, 為了方便, 沒有寫service, 直接寫的dao查詢數據庫

  struts.xml

    <package name="cookie-action" namespace="/" extends="struts-default">
        <action name="cookie_*" class="top.bwcx.cookie.action.LoginAction" method="{1}">
            <result name="loginUI">/WEB-INF/jsp/cookie/loginUI.jsp</result>
            <result name="successUI">/WEB-INF/jsp/cookie/successUI.jsp</result>
            <result name="success" type="redirectAction">
                <param name="actionName">cookie_successUI</param>
            </result>
        </action>
    </package>
View Code

  

  LoginAction.java

import java.net.URLEncoder;
import java.util.UUID;

import javax.servlet.http.Cookie;

import org.apache.struts2.ServletActionContext;

import top.bwcx.cookie.dao.UserDao;
import top.bwcx.cookie.entity.User;

import com.opensymphony.xwork2.ActionSupport;

@SuppressWarnings("serial")
public class LoginAction extends ActionSupport {
    private User user;
    private UserDao userDao = new UserDao();
    
    public String loginUI(){
        return "loginUI";
    }
    
    public String successUI(){
        return "successUI";
    }
    
    //用於表單登錄
    public String login(){
        try {
            if(user != null){//表單登錄
                //生成一串字符串與密碼關聯保存到cookie中, 並存到數據庫
                user = userDao.findByUsernameAndPassword(user);
                if(user != null){
                    //登錄成功, 將登錄信息保存在session中
                    ServletActionContext.getRequest().getSession().setAttribute("loginUser", user);
                    //生成一串uuid並保存到hobby中, 這裡應該是注冊就生成的, 可是這裡沒有注冊, 就第一次登錄的時候生成
                    //這個條件不用管, 根據你的實際情況判斷就行了
                    if(user.getHobby() == null || user.getHobby().equals("") || user.getHobby().contains("^")){
                        String hob = UUID.randomUUID().toString().replace("-", "");
                        user.setHobby(hob);
                        //更新
                        userDao.updateUser(user);
                    }
                    //將用戶名和uuid添加到cookie
                    String s = user.getUsername()+","+user.getHobby();
                    //編碼, 因為cookie不能包含逗號、分號或空格,也不能以 $ 字符開頭
                    String loginInfo = URLEncoder.encode(s, "UTF-8");    
                    Cookie loginCookie = new Cookie("loginInfo", loginInfo);
                    loginCookie.setMaxAge(60*60);
                    loginCookie.setPath("/");
                    ServletActionContext.getResponse().addCookie(loginCookie);
                }
            }else
                return "loginUI";


        } catch (Exception e) {
            e.printStackTrace();
        }
        return "success";
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
View Code

  我的表示本來就存在的表, 裡面有id, 用戶名, 密碼和愛好幾個字段, 我把愛好改成了由UUID生成的一串字符串, cookie自動登錄的時候就判斷這個字符串和用戶名. 我做了一個登陸的過濾器, cookie的自動登錄就放在裡面了

  setMaxAge(int expiry)表示cookie的過期時間, 負數表示關閉浏覽器cookie過期, 0表示立刻刪除cookie, 正數表示在該時間後過期, 單位秒.

  

  LoginFilter.java

/**
 * 服務器會為每個訪問它的的浏覽器創建一個session, 那麼服務器怎麼識別每個浏覽器呢? 這時就需要用到JSESSIONID, 所以說JSESSIONID的作用是:
 * JSESSIONID作為服務器識別每個浏覽器的唯一標識
 * 在浏覽器端將JSESSIONID刪除, 服務器端的session會失效
 */
public class LoginFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        String uri = req.getRequestURI();
//        System.out.println(uri);
        if(uri.contains("cookie_login"))
            chain.doFilter(req, resp);
        else{
            //查看session
            //登錄後在浏覽器端將JSESSIONID刪除, 看一下這裡的session
            Object user = req.getSession().getAttribute("loginUser");
            if(user == null){
                //如果session為空, 就嘗試cookie登錄
                boolean isLogin = CookieUtil.loginByCookies(req, resp);
                if(isLogin)
                    chain.doFilter(request, response);     //cookie登錄成功
                else
                    resp.sendRedirect(req.getContextPath()+"/cookie_loginUI.action");
            }
            else
                chain.doFilter(req, resp);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void destroy() {
    }
}
View Code

  

  用於cookie登錄的代碼

  CookieUtil.java

public class CookieUtil {
    private static UserDao userDao = new UserDao();

    public static boolean loginByCookies(HttpServletRequest request, HttpServletResponse response) {
        try {
            Cookie[] cookies = request.getCookies();
            if(cookies != null && cookies.length > 0){
                for (Cookie cookie : cookies) {
                    String cookieName = cookie.getName();
                    if(cookieName.equals("loginInfo")){
                    String cookieValue = URLDecoder.decode(cookie.getValue(), "UTF-8");
//                        System.out.println(cookieValue);
                        User user = userDao.findByUsernameAndHobby(cookieValue.split(",")[0], cookieValue.split(",")[1]);
                        if(user != null){
                            //登錄成功, 將登錄信息保存在session中
                            request.getSession().setAttribute("loginUser", user);
                            return true;
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
}
View Code

  

  以上是這個案例的主要代碼, 下面把一些其他用到的代碼也貼到下面

  UserDao.java

public class UserDao {
    public User findByUsernameAndPassword(User user) {
        try {
            String sql = "select * from user where username = ? and password = ?";
            return DbTools.getQueryRunner().query(sql, new BeanHandler<User>(User.class), user.getUsername(), user.getPassword());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public void updateUser(User user) {
        try {
            String sql = "update user set hobby = ? where id = ?";
            DbTools.getQueryRunner().update(sql, user.getHobby(), user.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public User findByUsernameAndHobby(String username, String hobby) {
        try {
            String sql = "select * from user where username = ? and hobby = ?";
            return DbTools.getQueryRunner().query(sql, new BeanHandler<User>(User.class), username, hobby);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
View Code

  

  操作數據庫用的是c3p0和dbutils, 這方面的內容可以在我以前的隨筆中找到, 裡面獲取QueryRunner對象時這樣得到的

public class DbTools {
    private static QueryRunner qr;
    static{
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        qr = new QueryRunner(dataSource);
    }
    public static QueryRunner getQueryRunner(){
        return qr;
    }
}
View Code

 

  User.java

public class User {
    private Integer id;
    private String username;
    private String password;
    private String hobby;
    
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getHobby() {
        return hobby;
    }
    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
}
View Code

  

  當然不要忘了web.xml的配置

  <!-- 登錄過濾器放在struts過濾器的前面 -->
  <filter>
      <filter-name>loginFilter</filter-name>
      <filter-class>top.bwcx.cookie.filter.LoginFilter</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>loginFilter</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <filter>
      <filter-name>struts2</filter-name>
      <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>*.action</url-pattern>
  </filter-mapping>
View Code

 

  這就是一個保存密碼並且能夠自動登錄的案例了. 由於本人水平有限, 如有錯誤不當之處請各位不吝指出.

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved