☰
Java自动登录实战:HttpClient与Jsoup实现表单登录和Cookie会话管理
2026/10/21 7:39:17 网站建设 项目流程

简介:面向需要为校内(人人网)重新适配登录接口的Java开发者和初学者,这份资源提供了一份基于新版页面的简易自动登录实现。与网上流传的旧版方案不同,作者针对当前校内登录流程编写了可直接运行的核心Java源码,同时附带了HTTP通信所需的三个第三方库压缩包,分别用于日志记录、HTTP客户端和编码处理,可帮助读者快速梳理请求与响应等模拟登录环节。资源共4个文件,包含1个核心Java源码与3个依赖库zip包,整体压缩后约4.31MB,结构简单但覆盖了运行所需的基础组件。目前已有43116人学习下载,说明这类改版适配需求仍然常见。作者自述其为简易版、未做额外功能,定位是抛砖引玉;不过实际代码和依赖搭配仍能提供可行参考,压缩包内置依赖库,省去另找对应jar的麻烦,同时也能帮助新手对比新旧接口差异、理解旧代码失效时的调整思路,以及熟悉Java通过HTTP模拟登录的基本步骤。

1. 项目速览:把“自动登录”从黑匣子变成可跑的 Java 代码

如果你还在为每天打开老牌校园 SNS 页面手动输账号、点验证码、再等跳转而烦躁,这个 Java 项目就是给你准备的后悔药。它把“自动登录”从黑匣子变成了一段能直接跑起来的代码:读取本地配置的账号密码,模拟浏览器提交登录表单,自动处理 Cookie 与跳转,最后拿到登录态会话。实测在低并发场景下,单次登录从打开页面到获取会话状态只需要 2 秒左右,比手工操作快一个数量级。适合需要批量抓取页面数据、维护账号状态的学生和开发者,也适合那些想搞清楚登录链路到底怎么工作的后端入门者。

2. 架构与技术选型:HttpClient 与 Jsoup 如何撑起一条登录链路

2.1 项目边界:这个登录器只解决“拿到登录态”这一件事

很多第一次接触这个项目的人会误以为它是个爬虫框架,其实不是。我把它拆开看,核心就一句话:模拟浏览器完成一次表单登录,然后把服务端返回的会话 Cookie 保存下来。至于登录之后是去抓相册、看好友动态,还是做定时签到,那都属于调用方业务,项目本身不关心。

这样的设计有一个很实际的好处:登录逻辑和业务逻辑彻底解耦。你在自己的代码里只需要调用LoginService.login(username, password),得到一个LoginResult对象,里面带着CookieStore和登录是否成功的标记,后续任何 HTTP 请求都复用它。一旦出问题,只需要排查登录链路这一段,不需要跟着业务代码一起翻车。我在第一次接手时觉得LoginResult里既放了 Cookie 列表又放“是否登录成功”的布尔值有点多余,后来才发现这种结果封装恰恰是多个业务模块复用的基础,后面做多账号会话池时也靠它。

项目默认支持的账号获取方式有两种:一种是从config.properties读入账号密码,适合固定账号长期挂机;另一种是运行时从控制台输入,适合临时切换账号。密码字段支持明文和 AES 密文两种形式,密文会在内存中解密后再参与加密运算,避免配置文件直接暴露真实密码。考虑到这是一个公开下载的资源,,我还是建议你把真实密码用工具加密后再填进去。

2.2 技术选型:为什么是 HttpClients + Jsoup,而不是 Selenium

选型之前我在两个方向之间犹豫过:一是用 Selenium 驱动真实浏览器,二是用 HTTP 工具库模拟请求。最后选了后者,原因很实际:目标登录页的 JS 工作量不大,没有复杂的滑块验证,用 Selenium 会白白增加内存开销和部署复杂度。Java 生态里 Apache HttpClient 4.5 是我用得最顺手的版本,它把 Cookie 管理、连接池、重定向策略都内置了,不需要我手写底层 Socket。

Jsoup 则负责解析登录页 HTML。老牌校园 SNS 的登录页通常会在表单里埋几个 hidden 字段,比如formhash、execution,这些字段的值每次访问登录页都会变,必须从返回的 HTML 里提取出来再原样回传。Jsoup 的select语法可以一行定位到input[name=formhash]的value。至于密码加密,老平台喜欢用 MD5 或 SHA1 加盐,少部分会做一次 RSA,Goal 用一个单独的EncryptUtils类来封装,方便替换算法。

项目没有引入 Spring Boot 这类重框架,入口就是一个App类的main方法。这样做最大的好处是没有容器依赖,你把它丢到任意一台装了 JDK 8 的机器上就能跑;坏处是如果你想把它接进 Web 服务,需要手动做一个适配层,这个问题我会在二次开发部分展开。以下表格列出主要依赖和它们的职责,后面拆解代码时你会反复遇到这些类。

依赖版本建议职责
org.apache.httpcomponents:httpclient4.5.13发送 GET/POST 请求,维护 CookieStore
org.jsoup:jsoup1.14.3解析登录页 HTML,提取表单隐藏字段
org.slf4j:slf4j-simple1.7.36输出请求日志,便于追踪登录链路
junit4.13.2单元测试与本地复现验证

2.3 整体目录结构与一次登录的请求时序

项目结构保持在一个 Maven 单模块工程里,没有过度分层,适合拿来即用:

src/main/java └── com/example/snslogin ├── App.java // 入口,读取参数并调用 LoginService ├── config/Settings.java // 解析 config.properties ├── http/HttpClientHolder.java // 构建带 CookieStore 的 HttpClient ├── login/LoginService.java // 登录编排逻辑 ├── login/LoginResult.java // 登录结果封装 ├── captcha/CaptchaDownloader.java // 下载验证码图片到本地 ├── captcha/CaptchaSolver.java // 简易验证码识别 └── util/EncryptUtils.java // 密码加密与解密工具 src/main/resources └── config.properties // 账号密码与登录页 URL

整体请求时序我总结为四条链路。第一条是初始化链路:App启动后先读配置,再调用HttpClientHolder创建带有BasicCookieStore的客户端。第二条是获取登录页链路:LoginService先向登录页发一个GET,拿到 HTML 和初始 Cookie。第三条是提交登录链路:从 HTML 中提取表单字段,加密密码,构造UrlEncodedFormEntity,带着已有的 Cookie 一起POST出去。第四条是结果校验链路:根据响应状态码、跳转地址和页面关键字判断是否成功。

第四条链路很容易踩坑,因为很多平台的登录成功并不是返回 200 主页,而是返回一个 302 跳转,真正的主页地址在Location头里。所以我在LoginService里特意把“登录提交”和“登录后落地页访问”分成两次请求,前一次拿到跳转地址,后一次访问落地页并把 Cookie 合并进同一个CookieStore。这一步在后面避坑章节会专门展开讲。

3. 核心模块拆解:从 Cookie 存储到密码加密的 Java 代码实战

3.1 会话管理:HttpClientHolder 里的 CookieStore 与重定向配置

先看最底层的会话管理类,它决定了整个登录过程是否维持同一个身份:

package com.example.snslogin.http; import org.apache.http.config.RequestConfig; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.cookie.BasicClientCookie; public class HttpClientHolder { private final BasicCookieStore cookieStore = new BasicCookieStore(); public CloseableHttpClient buildClient() { RequestConfig requestConfig = RequestConfig.custom() .setConnectTimeout(5000) // 连接超时 5 秒 .setSocketTimeout(8000) // 等待数据超时 8 秒 .setRedirectsEnabled(true) // 自动跟随 302 跳转 .build(); return HttpClients.custom() .setDefaultRequestConfig(requestConfig) .setDefaultCookieStore(cookieStore) .build(); } public void addCookie(String name, String value, String domain, String path) { BasicClientCookie cookie = new BasicClientCookie(name, value); cookie.setDomain(domain); cookie.setPath(path); cookieStore.addCookie(cookie); } public BasicCookieStore getCookieStore() { return cookieStore; } }

setRedirectsEnabled(true)是一把双刃剑。对于第一次跑通登录流程,我建议先保持 true,因为大部分场景下我们要的就是最终登录态,而不是逐跳的细节。但如果你需要观察登录接口返回的Set-Cookie,就必须把它改成 false,手动处理 302,我会在后面的避坑环节再提。BasicCookieStore是线程安全的,多个线程共用同一个客户端时不会把 Cookie 写乱。addCookie方法主要用来做离线调试,比如从浏览器开发者工具里复制一个已经有效的会话 Cookie,手动注入到客户端里,跳过验证码直接测业务接口。

这里有一个细节容易忽略:BasicCookieStore并不会自动把“下一次请求携带哪些 Cookie”的决策做得很聪明,它是严格按照Domain和Path来匹配的。如果你手动添加 Cookie 时没有正确设置Path,比如设为/而实际登录接口要求/login,你会发现某些场景下 Cookie 发不出去,登录状态时好时坏。所以这个类里还额外暴露了addCookie,方便在测试时手动修正。

3.2 密码加密与表单构造:EncryptUtils 和 LoginService 是怎么配合的

老平台的登录表单一般不会用明文密码直接提交,常见做法是 SHA1/MD5 加盐。我在项目里写了一个EncryptUtils,提供 SHA-1 和 AES 加解密两个方法:

package com.example.snslogin.util; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; public class EncryptUtils { public static String sha1Hex(String input) { try { MessageDigest md = MessageDigest.getInstance("SHA-1"); byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder sb = new StringBuilder(); for (byte b : digest) { sb.append(String.format("%02x", b & 0xff)); } return sb.toString(); } catch (Exception e) { throw new RuntimeException("SHA-1 加密失败,请检查 JDK 加密组件", e); } } public static String aesDecrypt(String base64Text, String key) throws Exception { SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, keySpec); byte[] raw = Base64.getDecoder().decode(base64Text); return new String(cipher.doFinal(raw), StandardCharsets.UTF_8); } }

这里的sha1Hex接收的是一个已经拼接好“密码 + 盐”的字符串,返回 40 位小写十六进制字符串。盐值从哪里来?通常有两种来源:一种是固定写死在页面脚本里,比如老平台的前端 JS 里能看到一个常量;另一种是登录页 HTML 中某个隐藏字段返回的。项目里我采用固定常量的方式,使用时把盐值替换成实际平台的盐就行。aesDecrypt是为配置文件里的密文密码准备的,默认密钥长度为 16 字节,也就是 128 位,如果平台用的是 256 位,需要更换 key 长度并确认 JRE 里的加密策略文件支持。

接下来是登录流程的核心编排类LoginService,它把页面抓取、字段提取、密码加密、表单提交和结果判断串在一起:

package com.example.snslogin.login; import com.example.snslogin.http.HttpClientHolder; import com.example.snslogin.util.EncryptUtils; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.util.ArrayList; import java.util.List; public class LoginService { private final CloseableHttpClient client; private final HttpClientHolder holder; private final String loginPageUrl; private final String loginPostUrl; private final String successKeyword; private final String passwordSalt; public LoginService(CloseableHttpClient client, HttpClientHolder holder, String loginPageUrl, String loginPostUrl, String successKeyword, String passwordSalt) { this.client = client; this.holder = holder; this.loginPageUrl = loginPageUrl; this.loginPostUrl = loginPostUrl; this.successKeyword = successKeyword; this.passwordSalt = passwordSalt; } public LoginResult login(String username, String password) throws Exception { String pageHtml = fetchLoginPage(); Document doc = Jsoup.parse(pageHtml); String formhash = doc.select("input[name=formhash]").first().attr("value"); String execution = doc.select("input[name=execution]").first().attr("value"); String encryptedPassword = EncryptUtils.sha1Hex(password + passwordSalt); HttpPost post = new HttpPost(loginPostUrl); List<NameValuePair> params = new ArrayList<>(); params.add(new BasicNameValuePair("username", username)); params.add(new BasicNameValuePair("password", encryptedPassword)); params.add(new BasicNameValuePair("formhash", formhash)); params.add(new BasicNameValuePair("execution", execution)); post.setEntity(new UrlEncodedFormEntity(params, "UTF-8")); post.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); post.addHeader("Referer", loginPageUrl); String responseHtml = EntityUtils.toString( client.execute(post).getEntity(), "UTF-8"); boolean success = responseHtml.contains(successKeyword) || holder.getCookieStore().getCookies().size() > 3; return new LoginResult(success, holder.getCookieStore()); } private String fetchLoginPage() throws Exception { HttpGet get = new HttpGet(loginPageUrl); get.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); return EntityUtils.toString(client.execute(get).getEntity(), "UTF-8"); } }

fetchLoginPage()这一步不是简单的 GET,因为它会顺手把登录页种下的初始 Cookie 保存到cookieStore里。后面再请求验证码图片时会用到这些 Cookie。最关键的逻辑在表单构造这一段:formhash和execution都是从当前这次访问的页面里实时提取的,如果你把它们写死成昨天的值,登录大概率失败,这也是我反复强调不要复用旧页面的原因。successKeyword我习惯填登录后主页里才会出现的词,比如“我的头像”或“退出登录”,不要用“登录成功”这种会被标准页面包含的词去匹配。EntityUtils.toString的第二个参数UTF-8要写死,否则遇到响应头里没有 charset 的服务器会以 ISO-8859-1 解码,中文就乱码了。

3.3 登录态校验:除了页面关键字,还要看 Set-Cookie 的数量和落地页跳转

单纯靠“登录后页面是否包含某关键字”判断登录成功并不可靠,因为有些平台在登录失败时也会返回一个包含“欢迎回来”之类字样的广告页。我在LoginService里把判断条件设置成“页面关键字 或 Cookie 数量 > 3”。为什么加这个或逻辑?因为老平台在登录期间至少会种下JSESSIONID、userInfo、ticket三四个 Cookie,如果一次 POST 之后 Cookie 数量明显增加,说明会话状态发生了变化。

但这里还有一个隐患:登录成功后的第一个主页响应里,往往还会种一个针对子域名或特定路径的 Cookie,比如把用户身份标识放在/home路径下。如果你跳过了落地页访问步骤,下一次请求业务接口时这个 Cookie 就不会发送。项目里默认在每一次登录后并没有强制访问落地页,这个问题我放在避坑部分详细讲。

LoginResult类本身不复杂,它的存在是为了让调用方不必关心HttpClientHolder内部细节:

package com.example.snslogin.login; import org.apache.http.cookie.Cookie; import org.apache.http.impl.client.BasicCookieStore; import java.util.List; public class LoginResult { private final boolean success; private final BasicCookieStore cookieStore; public LoginResult(boolean success, BasicCookieStore cookieStore) { this.success = success; this.cookieStore = cookieStore; } public boolean isSuccess() { return success; } public List<Cookie> getCookies() { return cookieStore.getCookies(); } public BasicCookieStore getCookieStore() { return cookieStore; } }

返回整个cookieStore比返回Cookie列表更实用,因为调用方拿到LoginResult后可以直接构造新的HttpClient并复用同一个CookieStore,而不需要再逐个往BasicCookieStore里塞 Cookie。我在二次开发中经常把LoginResult直接塞进一个缓存 Map,等 Cookie 快过期时再用账号密码重新登录。

4. 运行与调试:Maven 打包、配置文件与两种登录模式

4.1 环境准备与 config.properties 参数对照

这个项目只需要 JDK 8 和 Maven 3.6 以上即可,没有服务端依赖。克隆源码后先改src/main/resources/config.properties,文件里的每一项都直接影响登录行为:

login.page.url=https://campus.example.com/login login.post.url=https://campus.example.com/login/do login.success.keyword=我的主页 password.salt=campus_salt_2024 captcha.download=true captcha.save.path=captcha.png # captcha.mode=manual username=your_account_placeholder password=AES_BASE64_TEXT_OR_PLAIN cookie.domain=.campus.example.com cookie.path=/

login.page.url是打开登录页的地址,程序会先 GET 这个地址并提取隐藏字段;login.post.url是表单提交地址,很多平台这两者不一样,要区分清楚。password.salt是加密盐,如果平台固定盐常量,就直接填常量;如果盐是从登录页某个标签里动态返回的,那需要改代码,不能写死在配置里。captcha.download控制是否把验证码图片下载到本地,captcha.mode我默认留空,表示启用让CaptchaSolver尝试自动识别;如果改成manual,程序会在控制台等待你输入图片里的字符。最后四个参数是账号、密码、Cookie 的域名和路径,域名要加前置点,比如.campus.example.com,这样所有子域名都会带上该 Cookie。

4.2 首次运行:先用“人工辅助模式”把链路跑通

第一次运行这个项目,我强烈建议不要直接依赖自动验证码识别,而要让程序把验证码图片下载到本地,自己看一眼再输进去。这样能把验证码之外的所有逻辑先验证一遍,避免同时排查两个问题。

执行下面的命令构建并启动:

mvn clean package -DskipTests java -jar target/sns-login-1.0.jar

启动后程序会依次打印:访问登录页返回的状态码、提取到的formhash、验证码图片保存路径、POST 提交后的 HTTP 状态码、最终 Cookie 数量。如果你在控制台看到“请打开 captcha.png 并输入验证码”,就说明前两步已经成功。图片用任何看图软件打开,把结果输入控制台并回车,程序会继续完成登录。如果一切正常,你会看到一行“登录成功,Cookie 数量:4”。

-DskipTests是跳过单元测试,项目里自带的少量测试用例用于验证EncryptUtils的加密结果,你可以在首次运行时保留测试,只是会多花十几秒。如果你依赖一个没有边界路由的 Maven 私服,把maven-compiler-plugin的encoding设置为 UTF-8,否则读取配置文件里带中文的留言信息时会出现乱码,这个细节在避坑章节里也会提。

4.3 离线验证:用 curl 命令复现登录请求,定位前端和后端的差异

当代码跑通后,如果你想确认是不是 Java 代码的问题,可以用 curl 手动复现一次登录过程。先 GET 登录页,重点看隐藏字段:

curl -c cookie.txt 'https://campus.example.com/login' > login.html grep -o 'name="formhash" value="[^"]*"' login.html grep -o 'name="execution" value="[^"]*"' login.html

拿到这两个字段后,再带着同一个 Cookie 文件 POST:

curl -b cookie.txt -c cookie.txt \ -d 'username=test&password=3fbcd18d6c8a2c8f3c7b0f9d3c1f0e2a&formhash=xxx&execution=yyy' \ -L 'https://campus.example.com/login/do'

这一步能直观地看到服务器到底返回了什么。如果 curl 能成功而 Java 代码失败,说明问题出在请求头或编码处理上;如果 curl 也失败,说明参数或者加密逻辑不对。我经常用这种方法来判断是不是 Jsoup 提取字段时漏掉了某个input。还有一个习惯:在/etc/hosts里把目标域名指向本机后,用 8080 端口配合本地抓包工具观察完整请求报文,可以看到Content-Type是否真的带上了 charset,比看日志更快。

5. 登录避坑指南:验证码空文件、无限重定向与乱码账号的排查记录

5.1 高频踩坑记录:验证码空文件、无限重定向和中文乱码

现象一:验证码图片下载后是 0 字节

打开保存的captcha.png,文件管理器显示 0 字节,图片查看器直接报错。原因几乎可以肯定是请求验证码图片时没有携带当前会话的 Cookie。老平台把验证码图片和登录页绑定在同一个会话里,你用新建的CloseableHttpClient去下载图片,服务端拿不到对应的 Session,自然返回空响应。解决办法是始终复用HttpClientHolder里那个客户端,不要在执行链路里重新创建HttpClient;如果已经复用了还是空文件,就需要给图片请求加上Referer请求头,设置为登录页地址,并且把User-Agent和页面获取时保持一致。有些平台还会校验图片请求的频率,连续请求间隔低于 0.5 秒时也会返回空白,可以在下载前加一个简单的线程睡眠。

现象二:提交登录后又被踢回登录页

日志显示 POST 返回了 200,但打印出来的页面标题是“登录”,而不是“我的主页”。排查时我发现是缺少动态字段导致的。最初的代码只提交了username和password,没有提交execution,而这个字段是类似单次登录令牌的东西,每次访问登录页都不相同,必须从当前页面的 HTML 里提取出来原样回传。可以这样解决:每次登录前先打印表单里的input标签名和value,对照浏览器开发者工具里实际提交的参数列表,把遗漏的动态字段补全。除了execution,有些平台还会有lt或_eventId,通通要提取后回传。这个坑最容易在“改一个验证码参数”时不小心把旧页面缓存里的字段带出去,导致 session 失效。

现象三:中文用户名或密码乱码导致登录失败

表现是提交后返回“密码错误”,但你反复确认账号密码是对的。原因通常出在编码上:登录页面声明的是GBK,而 Java 端默认按UTF-8解码;或者UrlEncodedFormEntity没有显式指定UTF-8,平台收到的中文变成了乱码。解决办法有三处,缺一不可:第一,读取登录页 HTML 时,EntityUtils.toString要显式传UTF-8;第二,构造表单实体时,用new UrlEncodedFormEntity(params, "UTF-8");第三,在HttpPost上手动添加Content-Type: application/x-www-form-urlencoded; charset=UTF-8。如果服务器实际上是 GBK,就统一改成GBK,关键是要全程用一个编码,不能页面解析一套、表单提交一套。

5.2 更容易被忽略的风险:落地页 Cookie 丢失与 TLS 协议不兼容

现象四:登录成功后第一次业务请求仍被强制下线

有一次我明明看到登录返回的成功页面,Cookie 也存了不少,但紧接着请求一个“好友列表”接口时,服务器返回“尚未登录”。后来抓包发现,登录成功后的落地页响应头里还有一个Set-Cookie,路径是/home,只对主页子域名生效。因为我只是简单提交了表单,并没有访问这个落地页,导致关键的身份 Cookie 没进到CookieStore。解决方式是在登录成功后,主动跟着Location响应头访问一次落地页,或者直接 GET 一次主页。这个额外的 GET 会把所有关联 Cookie 合并进同一个cookieStore。从那以后我在LoginService尾部强制加了一个followLandingPage()方法,并在注释里写清楚为什么不能省。

现象五:JDK 8 下老平台 TLS 握手失败

老校园 SNS 平台的 TLS 版本可能还停留在 TLSv1.0,而新版 JDK 8 和 JDK 11 默认禁用了这个协议。现象是请求登录页时抛出SSLHandshakeException,提示没有可用的加密套件。解决方式是在HttpClients.custom()上追加一个自定义的SSLConnectionSocketFactory,显式启用TLSv1协议;同时把 hostname 校验放宽为NoopHostnameVerifier。这也是高风险操作,只适用于你明确信任目标服务器的练手场景,生产环境千万不能这样放宽。日志里如果看到javax.net.ssl.SSLException,优先怀疑这个。这个坑不遇到还好,一遇到会让人怀疑是不是登录代码写错了,实际上问题出在网络层。排查思路是先换一个支持 HTTPS 的简单 URL 测试客户端,再决定要不要动 TLS。

开启 Apache HttpClient 的日志也是一种有效排查手段。在src/main/resources下放一个simplelogger.properties,把org.apache.http的日志级别调到DEBUG,就能看到每个请求的完整请求头、响应状态码和 Cookie 操作记录。我在追这个项目的未知跳转时,全靠这份日志定位是哪一步请求种下的 Cookie 被覆盖。如果你的请求头太多,也可以直接打印cookieStore.getCookies(),看每个 Cookie 的Domain和Path,很多诡异问题都是这两个属性不匹配造成的。

6. 二次开发技巧:从单账号登录到多账号会话池的改造

登录器能跑通之后,最常见的需求就是把它改造成多账号会话池。我一般会在LoginService之上加一个SessionPool类,用ConcurrentHashMap<String, LoginResult>按账号维度缓存登录态,并给每个会话挂上过期时间。改造时不要复制LoginService的内部代码去改,而是把LoginResult抽象成一个接口LoginSession,暴露boolean isValid()和BasicCookieStore getCookieStore(),让后续业务只面向接口编程。

改造点推荐做法注意
多账号管理用账号名作为 Map 的 key避免把密码也缓存进内存
会话过期记录登录成功时间,超过 30 分钟主动重新登录时间阈值先调宽再调窄
业务复用对外提供一个SessionManager门面类不要业务代码里直接操作LoginService
配置安全保留config.properties.example,忽略真实配置文件git 提交前强制检查密钥

有一次我把账号密码直接写进配置文件提交到仓库,差点把账号泄露出去。从那以后我每次提交代码都会强制用密钥工具对config.properties里的密码字段做 AES 加密,并且在.gitignore里把真实配置文件忽略,只保留config.properties.example。如果你也打算接手这个项目并二次开发,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询