SpringBoot連接Microsoft SQL Server實(shí)現(xiàn)登錄驗(yàn)證
這篇文章將非常系統(tǒng)地講解如何使用 Spring Boot 結(jié)合 Microsoft SQL Server 2019 完成一個(gè)完整的登錄驗(yàn)證系統(tǒng),包括數(shù)據(jù)庫(kù)連接問(wèn)題、SSL證書(shū)錯(cuò)誤處理、CORS跨域詳細(xì)解釋和解決方案。
適合需要前后端聯(lián)調(diào)、單獨(dú)部署數(shù)據(jù)庫(kù)、獨(dú)立登錄服務(wù)的場(chǎng)景。
一、環(huán)境準(zhǔn)備
- 開(kāi)發(fā)工具:IDEA 2025.1
- Spring Boot 版本:3.4.3
- SQL Server 版本:Microsoft SQL Server 2019
- 瀏覽器:Chrome/Edge
技術(shù)選型
- Spring Boot Web
- JDBC 數(shù)據(jù)庫(kù)連接
- HTML + JavaScript 作為前端
項(xiàng)目結(jié)構(gòu)建議
BoounionLoginBackend/ ├── src/ │ └── main/ │ ├── java/com/example/boounion/ │ │ ├── controller/LoginController.java │ │ ├── model/User.java │ │ ├── service/UserService.java │ │ └── BoounionLoginBackendApplication.java │ └── resources/ │ └── static/index.html │ └── application.properties ├── pom.xml
二、數(shù)據(jù)庫(kù)配置詳解(application.properties)
spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=BoounionDB;encrypt=true;trustServerCertificate=true spring.datasource.username=sa spring.datasource.password=bl123456 spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver spring.jpa.hibernate.ddl-auto=none server.port=8080
重點(diǎn)解釋?zhuān)?/h3>
encrypt=true
:SQL Server 2019 默認(rèn)要求連接加密。如果不加,會(huì)連接失敗。trustServerCertificate=true
:如果你沒(méi)有配置 SSL 證書(shū),可以加上此參數(shù),忽略證書(shū)校驗(yàn),避免出現(xiàn) PKIX path building failed
錯(cuò)誤。server.port=8080
:設(shè)置后端啟動(dòng)端口為8080,確保和前端訪(fǎng)問(wèn)一致。
encrypt=true
:SQL Server 2019 默認(rèn)要求連接加密。如果不加,會(huì)連接失敗。trustServerCertificate=true
:如果你沒(méi)有配置 SSL 證書(shū),可以加上此參數(shù),忽略證書(shū)校驗(yàn),避免出現(xiàn) PKIX path building failed
錯(cuò)誤。server.port=8080
:設(shè)置后端啟動(dòng)端口為8080,確保和前端訪(fǎng)問(wèn)一致。連接 SQL Server 常見(jiàn)錯(cuò)誤總結(jié):
錯(cuò)誤提示 | 原因 | 解決方法 |
---|---|---|
TCP 1433端口連接失敗 | SQL Server未開(kāi)啟TCP/IP | 配置SQL Server網(wǎng)絡(luò)協(xié)議,啟用TCP/IP |
SSL 連接失敗 PKIX Error | 缺少受信任證書(shū) | 配置 encrypt=true 且 trustServerCertificate=true |
登錄失敗 | 用戶(hù)名密碼錯(cuò)誤 | 確認(rèn)賬號(hào)密碼正確且允許SQL登錄 |
遇到的錯(cuò)誤是典型的 SSL 證書(shū)驗(yàn)證失敗,錯(cuò)誤信息如下:
驅(qū)動(dòng)程序無(wú)法通過(guò)使用 SSL 與 SQL Server 建立安全連接。
錯(cuò)誤: PKIX path building failed: unable to find valid certification path
三、前端登錄界面(index.html)
頁(yè)面結(jié)構(gòu):
<input type="text" id="username" placeholder="用戶(hù)名"> <input type="password" id="password" placeholder="密碼"> <button onclick="login()">登錄</button>
美化 CSS 省略,重要的是 登錄函數(shù)邏輯。
登錄邏輯 JavaScript:
function login() { const username = document.getElementById("username").value; const password = document.getElementById("password").value; fetch("/api/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password }) }) .then(response => response.json().then(data => ({ status: response.status, body: data }))) .then(({ status, body }) => { if (status === 200 && body.status === "success") { window.location.href = body.redirectUrl; } else { alert(body.message || "登錄失敗"); } }) .catch(() => alert("請(qǐng)求失敗,請(qǐng)檢查網(wǎng)絡(luò)")); }
特點(diǎn):
- 使用 fetch() 發(fā)送 POST 請(qǐng)求,提交 JSON 格式用戶(hù)名和密碼。
- 登錄成功后,前端通過(guò) window.location.href 跳轉(zhuǎn)。
- 登錄失敗,使用 alert 彈窗提示用戶(hù)。
- 完全符合現(xiàn)代前后端分離開(kāi)發(fā)方式。
四、后端登錄接口(LoginController.java)
核心邏輯:
@PostMapping("/login") public ResponseEntity<Map<String, Object>> login(@RequestBody User user) { String result = userService.login(user); Map<String, Object> response = new HashMap<>(); switch (result) { case "登錄成功": response.put("status", "success"); response.put("redirectUrl", "https://www.baidu.com/s?wd=csdn"); return ResponseEntity.ok(response); case "密碼錯(cuò)誤": response.put("status", "error"); response.put("message", "密碼錯(cuò)誤"); return ResponseEntity.status(401).body(response); case "用戶(hù)不存在": response.put("status", "error"); response.put("message", "用戶(hù)不存在"); return ResponseEntity.status(404).body(response); default: response.put("status", "error"); response.put("message", "服務(wù)器異常"); return ResponseEntity.status(500).body(response); } }
亮點(diǎn)說(shuō)明:
- 使用 Map 封裝返回 JSON,靈活可擴(kuò)展。
- 成功返回 200 狀態(tài),帶 redirectUrl,由前端跳轉(zhuǎn)。
- 失敗返回具體的 401/404/500 狀態(tài)碼,便于前端判斷并提示。
? 保證登錄接口符合 RESTful API 設(shè)計(jì)規(guī)范。
五、數(shù)據(jù)庫(kù)驗(yàn)證邏輯(UserService.java)
簡(jiǎn)單查詢(xún)用戶(hù)密碼:
try (Connection conn = DriverManager.getConnection(dbUrl, dbUser, dbPassword)) { String sql = "SELECT password FROM Users WHERE username = ?"; try (PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setString(1, user.getUsername()); try (ResultSet rs = stmt.executeQuery()) { if (!rs.next()) return "用戶(hù)不存在"; if (!rs.getString("password").equals(user.getPassword())) return "密碼錯(cuò)誤"; return "登錄成功"; } } } catch (SQLException e) { e.printStackTrace(); return "數(shù)據(jù)庫(kù)錯(cuò)誤"; }
安全設(shè)計(jì)要點(diǎn):
- 使用 PreparedStatement,防止 SQL 注入攻擊。
- 明文密碼對(duì)比(后期建議增加加密如 BCrypt)。
- 連接和關(guān)閉資源使用 try-with-resources,避免泄漏。
六、跨域 CORS 問(wèn)題詳細(xì)解決
現(xiàn)象
前端 fetch 請(qǐng)求接口時(shí)報(bào)錯(cuò):
Access to fetch at ‘http://localhost:8080/api/login’ from origin ‘http://localhost:5500’ has been blocked by CORS policy.
原因
瀏覽器安全策略,跨域請(qǐng)求被攔截。
完整解決方案:
1. 配置全局允許跨域
創(chuàng)建 WebConfig.java
:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:5500") .allowedMethods("GET", "POST") .allowCredentials(true); } }
allowedOrigins
指定你的前端地址(可以是 IP、本地域名)。allowedMethods
指定支持的方法,如 POST、GET。allowCredentials(true)
允許帶上 cookie(如果未來(lái)需要)。
2. 單接口上臨時(shí)添加 @CrossOrigin
如果你只想允許登錄接口跨域:
@CrossOrigin(origins = "http://localhost:5500") @PostMapping("/login")
? 推薦使用全局配置統(tǒng)一處理,維護(hù)簡(jiǎn)單。
七、總結(jié)
- 數(shù)據(jù)庫(kù)連接重點(diǎn):SQL Server 需要
encrypt=true
,如果無(wú)證書(shū),加trustServerCertificate=true
- 跨域問(wèn)題重點(diǎn):Spring Boot 后端必須添加 CORS 配置,否則前端 fetch 會(huì)被瀏覽器攔截。
- 前端處理重點(diǎn):fetch 請(qǐng)求成功后由前端自己 window.location.href 跳轉(zhuǎn)。
- 后端接口規(guī)范:登錄接口返回 200狀態(tài)+JSON,而不是 302重定向。
- 安全建議:生產(chǎn)環(huán)境密碼應(yīng)加密存儲(chǔ),使用 BCrypt 算法。
附錄:完整文件(可自行補(bǔ)全代碼)
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>BoounionERP</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <!-- Spring Boot 父項(xiàng)目 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.4.3</version> <relativePath/> </parent> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- Spring Boot Web 模塊(包含內(nèi)嵌 Tomcat) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 開(kāi)發(fā)工具(自動(dòng)重啟,非必須) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <!-- SQL Server JDBC 驅(qū)動(dòng) --> <dependency> <groupId>com.microsoft.sqlserver</groupId> <artifactId>mssql-jdbc</artifactId> <version>11.2.3.jre11</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
index.html ?
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>登錄界面</title> <style> body { font-family: Arial; padding: 50px; background-color: #f0f2f5; } .login-box { background-color: white; padding: 20px; width: 300px; margin: auto; border-radius: 5px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); } input { width: 100%; margin-bottom: 10px; padding: 8px; box-sizing: border-box; } button { width: 100%; padding: 8px; background-color: #4CAF50; color: white; border: none; cursor: pointer; } </style> </head> <body> <div class="login-box"> <h2>登錄</h2> <input type="text" id="username" placeholder="用戶(hù)名"> <input type="password" id="password" placeholder="密碼"> <button onclick="login()">登錄</button> </div> <script> function login() { const username = document.getElementById("username").value; const password = document.getElementById("password").value; fetch("/api/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password }) }) .then(response => response.json().then(data => ({ status: response.status, body: data }))) .then(({ status, body }) => { if (status === 200 && body.status === "success") { window.location.href = body.redirectUrl; } else { alert(body.message || "登錄失敗"); } }) .catch(error => { alert("請(qǐng)求失敗,請(qǐng)檢查網(wǎng)絡(luò)"); console.error(error); }); } </script> </body> </html>
application.properties ?
# 數(shù)據(jù)庫(kù)連接配置 spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=BoounionDB;encrypt=true;trustServerCertificate=true spring.datasource.username=sa spring.datasource.password=bl123456 spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver # 其他常規(guī)配置 spring.jpa.hibernate.ddl-auto=none server.port=8080
Main.java ?
package org.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * ================================================== * This class Main is responsible for [功能描述]. * * @author darker * @version 1.0 * ================================================== */ @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } }
User.java ?
package org.example.model; /** * ================================================== * This class User is responsible for [功能描述]. * * @author darker * @version 1.0 * ================================================== */ public class User { private String username; private String password; public User() {} public User(String username, String password) { this.username = username; this.password = password; } 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; } }
UserService.java ?
package org.example.service; import org.example.model.User; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.sql.*; /** * ================================================== * This class UserService is responsible for [功能描述]. * * @author darker * @version 1.0 * ================================================== */ @Service public class UserService { @Value("${spring.datasource.url}") private String dbUrl; @Value("${spring.datasource.username}") private String dbUser; @Value("${spring.datasource.password}") private String dbPassword; public String login(User user) { try (Connection conn = DriverManager.getConnection(dbUrl, dbUser, dbPassword)) { String checkUserSql = "SELECT password FROM Users WHERE username = ?"; try (PreparedStatement stmt = conn.prepareStatement(checkUserSql)) { stmt.setString(1, user.getUsername()); try (ResultSet rs = stmt.executeQuery()) { if (!rs.next()) { return "用戶(hù)不存在"; } String dbPassword = rs.getString("password"); if (!dbPassword.equals(user.getPassword())) { return "密碼錯(cuò)誤"; } return "登錄成功"; } } } catch (SQLException e) { e.printStackTrace(); return "數(shù)據(jù)庫(kù)錯(cuò)誤"; } } }
LoginController.java ?
package org.example.controller; import org.example.model.User; import org.example.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; /** * ================================================== * This class LoginController is responsible for [功能描述]. * * @author darker * @version 1.0 * ================================================== */ @RestController @RequestMapping("/api") public class LoginController { @Autowired private UserService userService; @PostMapping("/login") public ResponseEntity<Map<String, Object>> login(@RequestBody User user) { String result = userService.login(user); Map<String, Object> response = new HashMap<>(); switch (result) { case "登錄成功": // 拼接帶參數(shù)的跳轉(zhuǎn)地址 /* String url = String.format( "https://www.baidu.com/s?username=%s", user.getUsername() ); */ String url = "https://www.baidu.com/s?wd=csdn"; response.put("status", "success"); response.put("redirectUrl", url); return ResponseEntity.ok(response); case "密碼錯(cuò)誤": response.put("status", "error"); response.put("message", "密碼錯(cuò)誤"); return ResponseEntity.status(401).body(response); case "用戶(hù)不存在": response.put("status", "error"); response.put("message", "用戶(hù)不存在"); return ResponseEntity.status(404).body(response); default: response.put("status", "error"); response.put("message", "服務(wù)器異常"); return ResponseEntity.status(500).body(response); } } }
到此這篇關(guān)于SpringBoot連接Microsoft SQL Server實(shí)現(xiàn)登錄驗(yàn)證的文章就介紹到這了,更多相關(guān)SpringBoot連SQL登錄驗(yàn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
ByteArrayOutputStream與InputStream互相轉(zhuǎn)換方式
這篇文章主要介紹了ByteArrayOutputStream與InputStream互相轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12關(guān)于CommandLineRunner的使用詳解
本文介紹了如何在SpringBoot項(xiàng)目啟動(dòng)時(shí)使用CommandLineRunner和ApplicationRunner接口進(jìn)行數(shù)據(jù)預(yù)加載或操作,通過(guò)實(shí)現(xiàn)這兩個(gè)接口,可以在項(xiàng)目啟動(dòng)時(shí)執(zhí)行特定的任務(wù),同時(shí),還展示了如何使用@Order注解來(lái)控制多個(gè)實(shí)現(xiàn)類(lèi)的加載順序2024-12-12Java在長(zhǎng)字符串中查找短字符串的實(shí)現(xiàn)多種方法
這篇文章主要介紹了Java在長(zhǎng)字符串中查找短字符串的實(shí)現(xiàn)多種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12springmvc 獲取@Requestbody轉(zhuǎn)換的異常處理方式
這篇文章主要介紹了springmvc 獲取@Requestbody轉(zhuǎn)換的異常處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07在Spring Boot中加載初始化數(shù)據(jù)的實(shí)現(xiàn)
這篇文章主要介紹了在Spring Boot中加載初始化數(shù)據(jù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02springboot 如何設(shè)置端口號(hào)和添加項(xiàng)目名
這篇文章主要介紹了springboot設(shè)置端口號(hào)和添加項(xiàng)目名的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08Java調(diào)用python代碼的五種方式總結(jié)
這篇文章主要給大家介紹了關(guān)于Java調(diào)用python代碼的五種方式,在Java中調(diào)用Python函數(shù)的方法有很多種,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-09-09