- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
最近分析了一大学bbs登录时,加密的流程,在此自己用spring boot做了个类似的,在此记录下,感觉还是有点东西的。逻辑很简单:
登录页面:
输入用户名/密码后,这里是admin/admin
用Fiddler抓包是这样的:
这里的Password用了AES加密,这里前端从后端再加载页面的时候,就获取了aes的密钥和向量,当然这里可以用JS逆向,得到这个值(这里和某大学BBS一样)
对应的流程图是这样的:
程序结构如下:
代码如下:
LoginController.java
package cn.it1995.MyController;
import cn.it1995.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class LoginController {
@Autowired
LoginService loginService;
@GetMapping("/")
public String index(HttpServletResponse response,
Model model){
String cookie = loginService.generateCookie();
String salt = loginService.getSaltByCookie(cookie);
String offset = loginService.getOffsetByCookie(cookie);
response.addCookie(new Cookie("token", cookie));
model.addAttribute("saltStr", salt);
model.addAttribute("offsetStr", offset);
return "login.html";
}
@PostMapping
public String login(@RequestParam("userName") String userName,
@RequestParam("password") String password,
HttpServletRequest request,
HttpServletResponse response){
Cookie[] cookies = request.getCookies();
String cookie = "";
for(Integer i = 0; i < cookies.length; i++){
if(cookies[i].getName().equals("token")){
cookie = cookies[i].getValue();
}
}
if(cookie.equals("")){
return "redircet:/";
}
// String salt = loginService.getSaltByCookie(cookie);
try{
boolean passwordCorrect = loginService.isPasswordCorrect(cookie, password);
System.out.println("The passwordCorrect is " + passwordCorrect);
if(!passwordCorrect){
return "redirect:/";
}
}
catch (Exception e){
e.printStackTrace();
return "redirect:/";
}
// System.out.println(userName);
// System.out.println(password);
// System.out.println(salt);
return "success.html";
}
}
MySession.java
package cn.it1995.Object;
public class MySession {
public String cookie;
public String salt;
public String offset;
public MySession(String cookie, String salt, String offset) {
this.cookie = cookie;
this.salt = salt;
this.offset = offset;
}
}
SessionRepository.java
package cn.it1995.repository;
import cn.it1995.Object.MySession;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
@Repository
public class SessionRepository {
private ArrayList<MySession> sessionList = new ArrayList<MySession>();
public void addSession(MySession session){
sessionList.add(session);
}
public String getSaltByCookie(String cookie){
for(MySession mySession : sessionList){
if(mySession.cookie.equals(cookie)){
return mySession.salt;
}
}
return null;
}
public String getOffsetByCookie(String cookie){
for(MySession mySession : sessionList){
if(mySession.cookie.equals(cookie)){
return mySession.offset;
}
}
return null;
}
public boolean isSessionExist(MySession session){
for(MySession mySession : sessionList){
if(mySession.cookie.equals(session.cookie)){
return true;
}
}
return false;
}
public void clearAllSession(){
sessionList.clear();
}
}
LoginServer.java
package cn.it1995.service;
import cn.it1995.Object.MySession;
import cn.it1995.repository.SessionRepository;
import cn.it1995.tool.AESUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Random;
@Service
public class LoginService {
@Autowired
SessionRepository sessionRepository;
public String generateCookie(){
String cookie = generateString(32);
String salt = generateString(16);
String offset = generateString(16);
MySession mySession = new MySession(cookie, salt, offset);
sessionRepository.addSession(mySession);
return cookie;
}
public String getSaltByCookie(String cookie){
return sessionRepository.getSaltByCookie(cookie);
}
public String getOffsetByCookie(String cookie){
return sessionRepository.getOffsetByCookie(cookie);
}
public boolean isUserExist(String userName){
if(userName.equals("admin")){
return true;
}
return false;
}
public boolean isPasswordCorrect(String cookie, String password) throws Exception {
String saltByCookie = sessionRepository.getSaltByCookie(cookie);
String vi = sessionRepository.getOffsetByCookie(cookie);
String decrypt = AESUtil.decrypt(password, saltByCookie, vi);
if(decrypt.equals("admin")){
return true;
}
return false;
}
public void removeAllSession(){
sessionRepository.clearAllSession();
}
protected String generateString(Integer length){
String str = "zxcvbnmlkjhgfdsaqwertyuiopQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
Random random = new Random();
//cookie
StringBuffer cookieSb = new StringBuffer();
for(int i = 0; i < length; ++i){
//产生0-61的数字
int number = random.nextInt(62);
//将产生的数字通过length次承载到sb中
cookieSb.append(str.charAt(number));
}
return new String(cookieSb);
}
}
AESUtil.java
package cn.it1995.tool;
import org.apache.tomcat.util.codec.binary.Base64;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
public class AESUtil {
public static String decrypt(String content, String key, String vi) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
Key k = toKey(key.getBytes());
byte[] encoded = k.getEncoded();
SecretKeySpec aes = new SecretKeySpec(encoded, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
IvParameterSpec iv = new IvParameterSpec(vi.getBytes());
cipher.init(Cipher.DECRYPT_MODE, aes, iv);
byte[] bytes = cipher.doFinal(Base64.decodeBase64(content));
return new String(bytes, "UTF-8");
}
public static String encrypt(String data, String key, String vi) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
//Key k = toKey(Base64.decodeBase64(key));
Key k = toKey(key.getBytes());
byte[] encoded = k.getEncoded();
SecretKeySpec aes = new SecretKeySpec(encoded, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
IvParameterSpec iv = new IvParameterSpec(vi.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, aes, iv);
byte[] bytes = cipher.doFinal(data.getBytes("UTF-8"));
return Base64.encodeBase64String(bytes);
}
private static Key toKey(byte[] key){
SecretKeySpec aes = new SecretKeySpec(key, "AES");
return aes;
}
public static void main(String[] args) throws NoSuchPaddingException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidKeyException {
String content = "7mv2MJPHj1o/rdar1I4i0Q==";
String key = "sN1DEJAVZNf3OdM3";
String vi = "GDHgt7hbKpsIR4b4";
System.out.println("原文 : root");
String e = AESUtil.encrypt("root", key, vi);
System.out.println("密文 : " + e);
String f = AESUtil.decrypt(e, key, vi);
System.out.println("解密 : " + f);
}
}
WebAppMain.java
package cn.it1995;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class WebAppMain {
public static void main(String[] args) {
SpringApplication.run(WebAppMain.class, args);
}
@Bean
public TomcatServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint constraint = new SecurityConstraint();
constraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
constraint.addCollection(collection);
context.addConstraint(constraint);
}
};
tomcat.addAdditionalTomcatConnectors(httpConnector());
return tomcat;
}
@Bean
public Connector httpConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
//Connector监听的http的端口号
connector.setPort(8081);
connector.setSecure(false);
//监听到http的端口号后转向到的https的端口号
connector.setRedirectPort(8443);
return connector;
}
}
login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<!-- Standard Meta -->
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<!-- Site Properties -->
<title>Login Example - Semantic</title>
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/reset.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/site.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/container.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/grid.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/header.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/image.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/menu.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/divider.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/segment.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/form.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/input.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/button.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/list.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/message.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/icon.css">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://semantic-ui.com/dist/components/form.js"></script>
<script src="https://semantic-ui.com/dist/components/transition.js"></script>
<!-- 引入 CDN Crypto.js 开始 AES加密 注意引入顺序 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/enc-base64.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/md5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/evpkdf.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/cipher-core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/aes.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/pad-pkcs7.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/mode-ecb.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/enc-utf8.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/enc-hex.min.js"></script>
<!-- 引入 CDN Crypto.js 结束 -->
<style type="text/css">
body {
background-color: #DADADA;
}
body > .grid {
height: 100%;
}
.image {
margin-top: -100px;
}
.column {
max-width: 450px;
}
</style>
<script language="JavaScript">
function login(){
var password = document.getElementById("password").value;
var offset = document.getElementById("offset").value;
var salt = document.getElementById("salt").value;
let key = CryptoJS.enc.Utf8.parse(salt);
let srcs = CryptoJS.enc.Utf8.parse(password);
let vi = CryptoJS.enc.Utf8.parse(offset);
var encrypted = CryptoJS.AES.encrypt(srcs, key, {
iv: vi,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
console.log("原始数据 : ", password);
console.log("vi : ", offset);
console.log("salt : ", salt);
document.getElementById("password").value = encrypted.toString();
console.log("ase加密 : ", encrypted.toString());
console.log("解密");
let decrypted = CryptoJS.AES.decrypt(encrypted.toString(), key, {
iv: vi,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
console.log("ase 解码 : " , CryptoJS.enc.Utf8.stringify(decrypted).toString());
}
</script>
</head>
<body>
<div class="ui middle aligned center aligned grid">
<div class="column">
<h2 class="ui teal image header">
<div class="content">
Log-in to your account
</div>
</h2>
<form action="login" method="post" class="ui large form" onsubmit="login()">
<div class="ui stacked segment">
<div class="field">
<div class="ui left icon input">
<i class="user icon"></i>
<input type="text" id="userName" name="userName" placeholder="userName">
</div>
</div>
<div class="field">
<div class="ui left icon input">
<i class="lock icon"></i>
<input type="password" id="password" name="password" placeholder="password">
</div>
</div>
<button class="ui fluid large teal button submit">Login</button>
</div>
<input name="salt" id="salt" type="hidden" th:value="${saltStr}">
<input name="offset" id="offset" type="hidden" th:value="${offsetStr}">
<div class="ui error message"></div>
</form>
</div>
</div>
</body>
</html>
success.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<!-- Standard Meta -->
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<!-- Site Properties -->
<title>Success</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/reset.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/site.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/container.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/grid.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/header.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/image.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/menu.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/divider.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/segment.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/form.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/input.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/button.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/list.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/message.css">
<link rel="stylesheet" type="text/css" href="https://semantic-ui.com/dist/components/icon.css">
<script src="https://semantic-ui.com/dist/components/form.js"></script>
<script src="https://semantic-ui.com/dist/components/transition.js"></script>
<style type="text/css">
body {
background-color: #DADADA;
}
body > .grid {
height: 100%;
}
.image {
margin-top: -100px;
}
.column {
max-width: 450px;
}
</style>
</head>
<body>
<div class="ui middle aligned center aligned grid">
<div class="column">
<h1>Success</h1>
</div>
</div>
</body>
</html>
application.properties
#Thymeleaf配置
spring.thymeleaf.cache=false
spring.thymeleaf.encoding=utf-8
spring.thymeleaf.mode=HTML5
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
server.port=8443
server.ssl.key-alias=selfsigned_localhost_sslserver
server.ssl.key-password=it1995
server.ssl.key-store=classpath:ssl-server.jks
server.ssl.key-store-provider=SUN
server.ssl.key-store-type=JKS
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>LoginAuthority</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-ext-jdk16</artifactId>
<version>1.45</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.10.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
源码打包下载地址:
假设我有一个带有隐藏提交按钮的表单,我在其中输入值,然后我点击一个按钮,就会出现带有确认消息和确认按钮的对话框。当我单击“确认”按钮时,我还单击了表单中隐藏的提交按钮。这可能吗?我如何在 JQuery
我们正在学习 Git 并使用 GitHub 作为我们的托管站点。 我们都 fork upstream repo 并 PR 我们的提交到 upstream 以获取我们的更改。 我们正在努力学习如何压缩我
我只需要一些关于这段代码的帮助。 var prv3; var markIt3 = function(e) { if (prv3 === this && this.checked) { th
如果 1 个表单使用“GET”方法而另一个使用“POST”方法,我如何提交位于同一页面上的 2 个表单。每个表单都有相同的操作并转到相同的下一页。需要帮忙。感谢大家的帮助。 我怎样才能得到下面这两个使
您好,我的表单中有以下脚本 function pdf() { var frm = document.getElementById("form1"); frm.action = "http://www.
我有一个 iOS 胖静态库(iphoneos 和 iphonesimulator),如果我在应用程序提交期间使用它,它会因为二进制文件包含 iphonesimulator 代码而失败吗? 最佳答案 我
我似乎有一个卡住的 git repo。它卡在所有基本的添加、提交命令上,git push 返回所有内容为最新的。 从其他帖子我已经完成了 git gc 和 git fsck/ 我认为基本的调试步骤是
我正在尝试发送由 jquery 创建的表单。该表单附加到一个 div 中,下面的变量“data”是使用 php 创建的,我将只发布最重要的 js 代码。 我尝试了很多带有和不带有“on()”的操作,但
我面临一个简单的问题,但不知道如何解决。我正在使用 twitter bootstrap 的标签。选项卡有效,但每个选项卡中的表单不提交。表单在没有选项卡的情况下提交。 以下是我用于标签的链接
我的计算机上有 140 个 git 存储库,每周我可以处理其中 10-15 个。有没有办法知道是否忘记提交/推送我的一个项目? 这些存储库都位于同一位置:“C:/Projects”。 输出类似于 C:
我对 javascript 完全陌生,目前正在开发我的第一个函数。我有这 2 个文本输入区域,可以在其中输入他的姓名和级别。 Nom: Niveau (1 á 6): 提交后,
我安装了最新的 Docker CS,得到了 LAMP image来自 Docker 集线器。我正在尝试在其中创建一个数据库并使用保存在其中的数据库制作一个新图像。 启动容器:docker run --
我有这个 jQuery 简单代码: 由于某种原因,submit() 无法正常工作(我的表单在单击 old_thumb 按钮后未提交。有人可以帮助我吗? 这里是 html 的一部分(它很长
如何获得 input type="submit"onclick 事件来触发 commitfunds.valdiate?我不能使用类或 ID。它必须是一个 onclick 事件。 这是代码: row A
关闭。这个问题需要debugging details .它目前不接受答案。 编辑问题以包含 desired behavior, a specific problem or error, and th
来自 this earlier thread我以为我知道可以使用 javascript submit() 命令通过 POST 方法发送表单数据。但我无法让它工作。这个演示在目的方面没有明显的意义,但请
在 mysql 重新启动时提交 XA 待处理事务时,出现以下错误。请帮助我解决这个错误。 mysql> XA RECOVER CONVERT XID; +----------+------------
我有一个带有 的表单. 如果启用了 Javascript,我将删除此 submit -输入字段$('#no-js-submit').remove();并添加“fire-ajax”按钮 $('Fire
我希望在页面加载后提交此表单,并且我使用了以下代码来完成此操作。问题是页面不断重新加载并停留在该循环中。 HTML Select Genre
我们有一个表单,其中有几个单独的提交按钮,它们执行不同的操作。问题是我有几个具有以下 HTML 的按钮: 现在您无法使用标准的 find_control 函数按值定位元素。所以我写了一个谓词函数来
我是一名优秀的程序员,十分优秀!