java 使用http协议实现登录及免密登录(使用cookie)

http服务器主类

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class HttpServerV3 {
    static class User {
        public String userName;
        public int age;
        public String school;
    }

    private ServerSocket serverSocket = null;
    // session 会话. 指的就是同一个用户的一组访问服务器的操作, 归类到一起, 就是一个会话.
    // 记者来采访你, 记者问的问题就是一个请求, 你回答的内容, 就是一个响应. 一次采访过程中
    // 涉及到很多问题和回答(请求和响应), 这一组问题和回答, 就可以称为是一个 "会话" (整个采访的过程)
    // sessions 中就包含很多会话. (每个键值对就是一个会话)
    private HashMap<String, User> session = new HashMap<>();

    public HttpServerV3(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动");
        //线程池
        ExecutorService executorService = Executors.newCachedThreadPool();

        while (true) {
            Socket clientSocket = serverSocket.accept();
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    process(clientSocket);
                }
            });
        }
    }

    public void process(Socket clientSocket) {
        //1. 读取请求并解
        try {
        HttpRequest request = HttpRequest.build(clientSocket.getInputStream());
        HttpResponse response = HttpResponse.build(clientSocket.getOutputStream());
        // 2. 根据请求计算响应
        // 此处按照不同的 HTTP 方法, 拆分成多个不同的逻辑

            if ("GET".equalsIgnoreCase(request.getMethod())) {
                doGet(request, response);
            } else if ("POST".equalsIgnoreCase(request.getMethod())) {
                doPost(request, response);
            } else {
                response.setStatus(405);
                response.setMessage("方法不明");
            }
            //把响应写会客户端
            response.flush();
        } catch (IOException | NullPointerException e) {
            e.printStackTrace();
        } finally {
            try {
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void doGet(HttpRequest request, HttpResponse response) throws IOException {
        // 1. 能够支持返回一个 html 文件.
        if (request.getUrl().startsWith("/index.html")) {
            String sessionId = request.getCookie("sessionId");
            User user = session.get(sessionId);
            if (sessionId == null || user == null) {
                // 说明当前用户尚未登陆, 就返回一个登陆页面即可.

                // 这种情况下, 就让代码读取一个 index.html 这样的文件.
                // 要想读文件, 需要先知道文件路径. 而现在只知道一个 文件名 index.html
                // 此时这个 html 文件所属的路径, 可以自己来约定(约定某个 d:/...) 专门放 html .
                // 把文件内容写入到响应的 body 中
                response.setStatus(200);
                response.setMessage("OK");
                response.setHeader("Content-Type", "text/html; charset=utf-8");
                InputStream inputStream = HttpServerV3.class.getClassLoader().getResourceAsStream("index.html");
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                // 按行读取内容, 把数据写入到 response 中
                String line = null;
                while ((line = bufferedReader.readLine()) != null) {
                    response.writeBody(line + "\n");
                }
                bufferedReader.close();
            } else {
                // 用户已经登陆, 无需再登陆了.
                response.setStatus(200);
                response.setMessage("OK");
                response.setHeader("Content-Type", "text/html; charset=utf-8");
                response.writeBody("<html>");
                response.writeBody("<div>" + "您已经登陆了! 无需再次登陆! 用户名: " + user.userName + "</div>");
                response.writeBody(+user.age + "</div>");
                response.writeBody("<div>" + user.school + "</div>");
                response.writeBody("</html>");
            }
        }
    }

    private void doPost(HttpRequest request, HttpResponse response) {
        // 2. 实现 /login 的处理
        if (request.getUrl().startsWith("/login")) {
            // 读取用户提交的用户名和密码
            String userName = request.getParameter("username");
            String password = request.getParameter("password");
//            System.out.println("userName: " + userName);
//            System.out.println("password: " + password);
            // 登陆逻辑就需要验证用户名密码是否正确.
            // 此处为了简单, 咱们把用户名和密码在代码中写死了.
            // 更科学的处理方式, 应该是从数据库中读取用户名对应的密码, 校验密码是否一致.
            if ("kevin".equals(userName) && "123".equals(password)) {
                // 登陆成功
                response.setStatus(200);
                response.setMessage("OK");
                response.setHeader("Content-Type", "text/html; charset=utf-8");
                // 原来登陆成功, 是给浏览器写了一个 cookie, cookie 中保存的是用户的用户名.
                // response.setHeader("Set-Cookie", "userName=" + userName);

                // 现有的对于登陆成功的处理. 给这次登陆的用户分配了一个 session
                // (在 hash 中新增了一个键值对), key 是随机生成的. value 就是用户的身份信息
                // 身份信息保存在服务器中, 此时也就不再有泄露的问题了
                // 给浏览器返回的 Cookie 中只需要包含 sessionId 即可
                String sessionId = UUID.randomUUID().toString();
                User user = new User();
                user.userName = "kevin";
                user.age = 20;
                user.school = "school";
                session.put(sessionId, user);
                response.setHeader("Set-Cookie", "sessionId=" + sessionId);

                response.writeBody("<html>");
                response.writeBody("<div>欢迎您! " + userName + "</div>");
                response.writeBody("</html>");
            } else {
                // 登陆失败
                response.setStatus(403);
                response.setMessage("Forbidden");
                response.setHeader("Content-Type", "text/html; charset=utf-8");
                response.writeBody("<html>");
                response.writeBody("<div>登陆失败</div>");
                response.writeBody("</html>");
            }
        }
    }

    public static void main(String[] args) throws IOException {
        HttpServerV3 serverV3 = new HttpServerV3(900);
        serverV3.start();
    }
}

处理请求

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

public class HttpRequest {
    private String method;
    private String url;
    private String version;
    private Map<String, String> headers = new HashMap<>();
    private Map<String, String> parameters = new HashMap<>();
    private Map<String, String> cookies = new HashMap<>();
    private String body;

    public static HttpRequest build(InputStream inputStream) throws IOException {
        HttpRequest request = new HttpRequest();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        //处理首行
        String firstLine = bufferedReader.readLine();
        String[] fistLineTokens = firstLine.split(" ");
        request.method = fistLineTokens[0];
        request.url = fistLineTokens[1];
        request.version = fistLineTokens[2];
        // 2. 解析 url
        int post = request.url.indexOf("?");
        if (post != -1) {
            String queryString = request.url.substring(post + 1);
            parseKv(queryString, request.parameters);
        }
        //循环处理header
        String line = "";
        while ((line = bufferedReader.readLine()) != null && line.length() != 0) {
            String[] headerToken = line.split(": ");
            request.headers.put(headerToken[0],headerToken[1]);
        }
        // 4. 解析 cookie
        String cookie = request.headers.get("Cookie");
        if (cookie != null) {
            // 把 cookie 进行解析
            parseCookie(cookie, request.cookies);
        }
        //5. 解析 body
        if ("POST".equalsIgnoreCase(request.method)
                || "PUT".equalsIgnoreCase(request.method)) {
            // 这两个方法需要处理 body, 其他方法暂时不考虑
            // 需要把 body 读取出来.
            // 需要先知道 body 的长度. Content-Length 就是干这个的.
            // 此处的长度单位是 "字节"
            int contentLength = Integer.parseInt(request.headers.get("Content-Length"));
            // 注意体会此处的含义~~
            // 例如 contentLength 为 100 , body 中有 100 个字节.
            // 下面创建的缓冲区长度是 100 个 char (相当于是 200 个字节)
            // 缓冲区不怕长. 就怕不够用. 这样创建的缓冲区才能保证长度管够~~
            char[] buffer = new char[contentLength];
            int len = bufferedReader.read(buffer);
            request.body = new String(buffer, 0, len);
            // body 中的格式形如: username=***&password=123
            parseKv(request.body, request.parameters);
        }
        return request;
        }

    private static void parseCookie(String cookie, Map<String, String> cookies) {
        // 1. 按照 分号空格 拆分成多个键值对
        String[] kvTokens = cookie.split(";");
        //2. 按照等号把键值拆分 放到cookies里面
        for ( String kv : kvTokens) {
            String[] result = kv.split("=");
            cookies.put(result[0],result[1]);
        }

    }

    private static void parseKv (String queryString, Map < String, String > parameters){
        // 1. 按照 & 拆分成多个键值对
        String[] kvTokens = queryString.split("&");
        for (String kv : kvTokens) {
            String[] result = kv.split("=");
            parameters.put(result[0], result[1]);
        }
    }

    public String getMethod() {
        return method;
    }

    public String getUrl() {
        return url;
    }

    public String getVersion() {
        return version;
    }

    public String getHeaders(String key) {
        return headers.get(key);
    }

    public String getParameter(String key) {
        return parameters.get(key);
    }

    public String getCookie(String key) {
        return cookies.get(key);
    }

    public String getBody() {
        return body;
    }
}

处理响应

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.Map;

public class HttpResponse {
    private String version = "HTTP/1.1";
    private int status;
    private String message;
    private Map<String, String> headers = new HashMap<>();
    private StringBuilder body = new StringBuilder();
    private OutputStream outputStream = null;

    public static HttpResponse build(OutputStream outputStream) {
        HttpResponse response = new HttpResponse();
        response.outputStream = outputStream;
        return response;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public void setHeader(String key, String value) {
        headers.put(key, value);
    }

    public void writeBody(String content) {
        body.append(content);
    }

    public void flush() throws IOException {
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write(version + " " + status + " " + message + "\n");
        headers.put("Content-Length", body.toString().getBytes().length + "");
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            bufferedWriter.write(entry.getKey() + ": " + entry.getValue() + "\n");
        }
        bufferedWriter.write("\n");
        bufferedWriter.write(body.toString());
        bufferedWriter.flush();
    }
}

登录页面html

<html>
<head>
    <title>登陆页面</title>
    <meta charset="utf-8">
</head>
<body>
<!-- 注释   -->
<form method="post" action="/login">
    <div style="margin-bottom: 10px">
        <input type="text" name="username" placeholder="请输入姓名">
    </div>
    <div style="margin-bottom: 10px">
        <input type="password" name="password" placeholder="请输入密码">
    </div>
    <div>
        <input type="submit" value="登陆">
    </div>
</form>
</body>
</html>

fiddler抓取数据包

登录界面

在这里插入图片描述

第一次登录成功

在这里插入图片描述

第二次使用cookie进行免密登录

在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现免密登录,可以使用Java中的SSH框架,如JSch。以下是一个简单的示例代码,用于连接到远程服务器并执行命令,而无需输入密码。 ```java import com.jcraft.jsch.*; public class SSHTest { public static void main(String[] args) { String host = "yourhost.com"; String user = "yourusername"; String password = "yourpassword"; try { JSch jsch = new JSch(); Session session = jsch.getSession(user, host, 22); session.setPassword(password); // Avoid asking for key confirmation java.util.Properties prop = new java.util.Properties(); prop.put("StrictHostKeyChecking", "no"); session.setConfig(prop); session.connect(); // Run command ChannelExec channelExec = (ChannelExec) session.openChannel("exec"); channelExec.setCommand("ls -lrt"); channelExec.connect(); // Get output InputStream in = channelExec.getInputStream(); byte[] tmp = new byte[1024]; while (true) { while (in.available() > 0) { int i = in.read(tmp, 0, 1024); if (i < 0) break; System.out.print(new String(tmp, 0, i)); } if (channelExec.isClosed()) { System.out.println("exit-status: " + channelExec.getExitStatus()); break; } try { Thread.sleep(1000); } catch (Exception ee) { } } channelExec.disconnect(); session.disconnect(); } catch (Exception e) { e.printStackTrace(); } } } ``` 在上面的代码中,我们使用JSch框架来连接到远程服务器。我们传递主机名,用户名和密码来创建一个会话。我们还使用setConfig()方法来禁用主机密钥确认。然后,我们使用connect()方法来连接到远程服务器。 接下来,我们使用openChannel()方法打开一个exec通道,并传递一个命令来执行。我们使用getInputStream()方法来获取命令的输出,并将其打印到控制台上。 最后,我们断开exec通道和会话连接。 请注意,这只是一个简单的示例代码,您需要根据您的需求进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值