Springboot企業級開發資料入門教程
Spring Boot企业级开发资料介绍了如何使用Spring Boot进行企业级应用开发,包括快速搭建项目、数据库集成、异步任务处理和WebSocket等关键功能。文章还详细讲解了Spring Boot的安全配置和JWT技术的应用。通过丰富的示例代码和配置,帮助开发者更好地理解和使用Spring Boot的各项特性。
Spring Boot企业级开发资料入门教程 Spring Boot简介Spring Boot是什么
Spring Boot 是一个微框架,它是 Spring 社区为简化新 Spring 应用程序的初始搭建和配置而提供的解决方案。它的设计目标是简化 Spring 应用的初始搭建及开发过程,使开发者能够快速编写单个模块的独立且生产级别的应用,无需编写大量配置代码。
Spring Boot的优势
- 自动配置:Spring Boot 可以根据应用类型自动配置 Spring 框架。
- 独立运行:支持打包可独立运行的应用,无需部署到应用服务器。
- 嵌入式服务器:内置了 Tomcat、Jetty 或 Undertow 作为应用服务器。
- 热部署:支持热部署,便于开发。
- 开发工具集成:与大多数开发工具和构建工具(如 Maven 和 Gradle)无缝集成。
- 监控与健康检查:内置监控和健康检查功能。
- 无代码生成:不需要额外的 XML 配置,支持约定优于配置,减少样板代码。
第一个Spring Boot项目创建
使用 Spring Initializr(可以访问 https://start.spring.io/)快速创建一个 Spring Boot 项目。选择以下选项:
- 项目类型:Maven Project
- Java 版本:8 及以上
- 语言:Java
- 依赖:Spring Web
- 包名:com.example.demo
- 项目名:demo
- 构建工具:Maven
生成的项目结构如下:
demo
│ .gitignore
│ pom.xml
│
└───src
└───main
├───java
│ └───com
│ └───example
│ └───demo
│ │ DemoApplication.java
│ └───controller
│ HelloController.java
└───resources
│ application.properties
└───static
DemoApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
HelloController.java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello, World!";
}
}
在 src/main/resources/application.properties
文件中,可以添加一些基本配置:
server.port=8080
使用 Maven 打包并运行:
mvn clean package
java -jar target/demo-0.0.1-SNAPSHOT.jar
快速上手Spring Boot
Maven和Gradle项目搭建
Maven项目搭建
使用 Maven 作为构建工具,需要在项目根目录下创建 pom.xml
文件。示例内容如下:
<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>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Example project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.5.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Gradle项目搭建
使用 Gradle 作为构建工具,需要在项目根目录下创建 build.gradle
文件。示例内容如下:
plugins {
id 'org.springframework.boot' version '2.5.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
Spring Boot启动器与自动配置
Spring Boot 通过启动器 (Spring Boot Starter
) 简化了依赖管理。启动器是包含一组预定义依赖项的特殊依赖项。例如,spring-boot-starter-web
包含创建 Spring Web 应用所需的所有依赖项。
自动配置 (Auto-configuration
) 是 Spring Boot 的另一个关键特性,它允许框架自动配置您的 Spring 应用程序。例如,spring-boot-starter-web
包含了一个自动配置类,可以检测 DispatcherServlet
是否已经配置,并自动配置它。
MVC架构与Thymeleaf模板引擎
Spring Boot 支持 MVC 架构,其中 DispatcherServlet
作为前端控制器,负责接收 HTTP 请求并根据请求将控制传递给相应的处理器。处理器处理请求并返回视图,视图将模型数据传递给视图解析器以生成响应。
Thymeleaf 模板引擎
Thymeleaf 是一个 Java 模板引擎,用于生成 HTML、XML、JavaScript、CSS、任意文本文件等。它通过注解来注入变量,并与 Spring Boot 配合使用,可以简化前端视图的开发。
示例代码
在 src/main/resources/templates
目录下创建 index.html
文件:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Boot Thymeleaf Example</title>
</head>
<body>
<h1 th:text="'Hello, ' + ${name} + '!'"></h1>
</body>
</html>
在 src/main/java/com/example/demo/controller
目录下创建 HomeController.java
:
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/")
public String home(Model model) {
model.addAttribute("name", "World");
return "index";
}
}
数据库集成
Spring Data JPA入门
Spring Data JPA 是 Spring Data 框架的一部分,简化了与 JPA(Java Persistence API)的集成。它允许开发者通过简单的方法名和类名来操作数据库。
示例代码
在 src/main/java/com/example/demo/entity
目录下创建 User.java
:
package com.example.demo.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// Getters and Setters
}
在 src/main/java/com/example/demo/repository
目录下创建 UserRepository.java
:
package com.example.demo.repository;
import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}
在 src/main/java/com/example/demo/service
目录下创建 UserService.java
:
package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public List<User> getAllUsers() {
return userRepository.findAll();
}
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
public User createUser(User user) {
return userRepository.save(user);
}
public User updateUser(Long id, User user) {
User existingUser = userRepository.findById(id).orElse(null);
if (existingUser != null) {
existingUser.setName(user.getName());
existingUser.setEmail(user.getEmail());
return userRepository.save(existingUser);
}
return null;
}
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
在 src/main/java/com/example/demo/controller
目录下创建 UserController.java
:
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public List<User> getAllUsers() {
return userService.getAllUsers();
}
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
@PostMapping
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
return userService.updateUser(id, user);
}
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}
在 src/main/resources/application.properties
中添加数据库配置:
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=update
JDBC与MyBatis基本使用
Spring Boot 也支持 JDBC 和 MyBatis,但 JPA 和 MyBatis 更加流行。
JDBC 示例
在 src/main/java/com/example/demo/repository
目录下创建 JdbcUserRepository.java
:
package com.example.demo.repository;
import com.example.demo.entity.User;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class JdbcUserRepository {
private JdbcTemplate jdbcTemplate;
public JdbcUserRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public List<User> getAllUsers() {
return jdbcTemplate.query("SELECT * FROM users", new UserRowMapper());
}
public User getUserById(Long id) {
return jdbcTemplate.queryForObject("SELECT * FROM users WHERE id = ?", new UserRowMapper(), id);
}
public User createUser(User user) {
jdbcTemplate.update("INSERT INTO users (name, email) VALUES (?, ?)", user.getName(), user.getEmail());
return user;
}
public User updateUser(Long id, User user) {
jdbcTemplate.update("UPDATE users SET name = ?, email = ? WHERE id = ?", user.getName(), user.getEmail(), id);
return user;
}
public void deleteUser(Long id) {
jdbcTemplate.update("DELETE FROM users WHERE id = ?", id);
}
private static class UserRowMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
return user;
}
}
}
MyBatis 示例
需要添加 mybatis-spring-boot-starter
依赖:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
在 src/main/resources/mapper/UserMapper.xml
中定义 SQL 语句:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.repository.UserRepository">
<select id="getAllUsers" resultType="com.example.demo.entity.User">
SELECT * FROM users
</select>
<select id="getUserById" resultType="com.example.demo.entity.User">
SELECT * FROM users WHERE id = #{id}
</select>
<insert id="createUser">
INSERT INTO users (name, email) VALUES (#{name}, #{email})
</insert>
<update id="updateUser">
UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}
</update>
<delete id="deleteUser">
DELETE FROM users WHERE id = #{id}
</delete>
</mapper>
在 src/main/java/com/example/demo/repository
目录下创建 UserRepository.java
:
package com.example.demo.repository;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.ResultMap;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface UserRepository {
@Select("SELECT * FROM users")
List<User> getAllUsers();
@Select("SELECT * FROM users WHERE id = #{id}")
User getUserById(@Param("id") Long id);
@Insert("INSERT INTO users (name, email) VALUES (#{name}, #{email})")
void createUser(User user);
@Update("UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}")
void updateUser(@Param("id") Long id, @Param("user") User user);
@Delete("DELETE FROM users WHERE id = #{id}")
void deleteUser(@Param("id") Long id);
}
数据库连接与事务管理
事务管理
Spring Boot 内置了事务管理功能,通过 @Transactional
注解来启用事务支持。
示例代码
在 src/main/java/com/example/demo/service
目录下创建 TransactionService.java
:
package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class TransactionService {
@Autowired
private UserRepository userRepository;
@Transactional
public void createUserWithTransaction(User user) {
userRepository.save(user);
// Simulate an exception to rollback the transaction
throw new RuntimeException("Simulated exception");
}
@Transactional
public void createUserWithoutException(User user) {
userRepository.save(user);
}
}
异步与WebSocket
Spring Boot异步任务处理
Spring Boot 支持异步任务处理,可以使用 @Async
注解来实现。
示例代码
在 src/main/java/com/example/demo/task
目录下创建 TaskService.java
:
package com.example.demo.task;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class TaskService {
@Async
public void performLongRunningTask() {
// Simulate a long-running task
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task completed");
}
}
在 src/main/java/com/example/demo/controller
目录下创建 AsyncTaskController.java
:
package com.example.demo.controller;
import com.example.demo.task.TaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AsyncTaskController {
@Autowired
private TaskService taskService;
@GetMapping("/async")
public String triggerAsyncTask() {
taskService.performLongRunningTask();
return "Task triggered asynchronously";
}
}
在 src/main/java/com/example/demo
目录下的 DemoApplication.java
中添加 @EnableAsync
注解:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
WebSocket基本概念
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。它让服务器端主动向客户端推送数据成为可能,从而可以更好地实现实时通信和推送功能。
WebSocket聊天室案例演示
在 src/main/java/com/example/demo
目录下创建 WebSocketConfig.java
:
package com.example.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(chatHandler(), "/chat").setAllowedOrigins("*").withInterceptors(new HttpSessionHandshakeInterceptor());
}
@Bean
public ChatHandler chatHandler() {
return new ChatHandler();
}
}
在 src/main/java/com/example/demo
目录下创建 ChatHandler.java
:
package com.example.demo;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ChatHandler extends TextWebSocketHandler {
private Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.put(session.getId(), session);
System.out.println("New client connected: " + session.getId());
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessions.remove(session.getId());
System.out.println("Client disconnected: " + session.getId());
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
System.out.println("Received message: " + payload);
// Broadcast the message to all connected clients
sessions.values().forEach(s -> {
if (s.isOpen()) {
try {
s.sendMessage(new TextMessage(payload));
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
在 src/main/resources/templates
目录下创建 chat.html
:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>WebSocket Chat</title>
<script>
var socket = new WebSocket("ws://localhost:8080/chat");
socket.onmessage = function(event) {
var message = document.createElement("div");
message.textContent = event.data;
document.getElementById("messages").appendChild(message);
};
function sendMessage() {
var input = document.getElementById("message");
socket.send(input.value);
input.value = "";
}
</script>
</head>
<body>
<h1>WebSocket Chat</h1>
<div id="messages"></div>
<input id="message" type="text" onkeypress="if (event.keyCode == 13) sendMessage();"/>
<button onclick="sendMessage()">Send</button>
</body>
</html>
在 src/main/java/com/example/demo/controller
目录下创建 ChatController.java
:
package com.example.demo.controller;
import com.example.demo.WebSocketConfig;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
@RestController
public class ChatController {
@GetMapping("/chat")
public ModelAndView chat() {
return new ModelAndView("chat");
}
}
日志与监控
Log4j2与SLF4J日志框架
SLF4J(Simple Logging Facade for Java)是一个简单的日志门面(Facade),它提供了多个实现(如 Log4j、Logback 等)的抽象。Spring Boot 默认使用 Logback 作为日志实现,但也可以配置使用 Log4j2。
示例代码
在 src/main/resources/log4j2.xml
中配置 Log4j2 日志框架:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<File name="File" fileName="app.log">
<PatternLayout pattern="%d{ABSOLUTE} %5p %c{1}:%L - %m%n"/>
</File>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="File"/>
</Root>
</Loggers>
</Configuration>
在 src/main/resources/application.properties
中启用 Log4j2 配置:
logging.config=classpath:log4j2.xml
在 src/main/java/com/example/demo
目录下的 DemoApplication.java
中添加日志输出:
package com.example.demo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
private static final Logger logger = LoggerFactory.getLogger(DemoApplication.class);
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
logger.info("Application Started");
}
}
Spring Boot Actuator监控
Spring Boot Actuator 是一个模块,它提供了生产就绪功能,例如健康检查、指标收集、审计、远程刷新等。它允许您更好地了解您的应用程序并管理它。
示例代码
在 pom.xml
或 build.gradle
文件中添加 Actuator 依赖:
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.5.3</version>
</dependency>
// build.gradle
implementation 'org.springframework.boot:spring-boot-starter-actuator:2.5.3'
在 src/main/resources/application.properties
中添加 Actuator 配置:
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
启动应用程序后,访问 /actuator
路径,可以看到所有暴露的端点。
热部署与Spring Boot DevTools
Spring Boot DevTools 提供了热部署功能,可以简化开发流程。
示例代码
在 pom.xml
或 build.gradle
文件中添加 DevTools 依赖:
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.5.3</version>
<scope>runtime</scope>
</dependency>
// build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-devtools:2.5.3'
}
使用 mvn spring-boot:run
或 gradle bootRun
命令启动应用,每次保存文件时,应用会自动重新启动。
Spring Security基本配置
Spring Security 是一个强大的安全框架,提供认证和授权功能。
示例代码
在 pom.xml
或 build.gradle
文件中添加 Security 依赖:
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.5.3</version>
</dependency>
// build.gradle
implementation 'org.springframework.boot:spring-boot-starter-security:2.5.3'
在 src/main/java/com/example/demo
目录下创建 SecurityConfig.java
:
package com.example.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("password")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder().encode("password")).roles("ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasRole("USER")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.permitAll();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
用户认证与授权
在 src/main/java/com/example/demo
目录下创建 SecurityController.java
:
package com.example.demo;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SecurityController {
@GetMapping("/user")
public Principal user(@AuthenticationPrincipal OidcUser oidcUser) {
return oidcUser;
}
}
在 src/main/resources/templates
目录下创建 login.html
:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form action="/login" method="POST">
<input type="text" name="username" placeholder="Username" required />
<input type="password" name="password" placeholder="Password" required />
<input type="submit" value="Login" />
</form>
</body>
</html>
认证令牌与JWT技术介绍
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用环境间安全地将信息作为JSON对象传输。
示例代码
在 src/main/java/com/example/demo/security
目录下创建 JwtAuthenticationFilter.java
:
package com.example.demo.security;
import at.favre.lib.crypto.bcrypt.BCrypt;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
private UserRepository userRepository;
public JwtAuthenticationFilter(AuthenticationManager authManager, UserRepository userRepository) {
super(authManager);
this.userRepository = userRepository;
}
@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
String token = request.getHeader("Authorization");
if (token != null) {
String username = Jwts.parser().setSigningKey("secret".getBytes()).parseClaimsJws(token).getBody().getSubject();
if (username != null) {
User user = userRepository.findByUsername(username);
if (user != null) {
Authentication auth = new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
}
filterChain.doFilter(request, response);
}
}
在 src/main/java/com/example.demo.security
目录下创建 JwtTokenProvider.java
:
package com.example.demo.security;
import at.favre.lib.crypto.bcrypt.BCrypt;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.stream.Collectors;
@Component
public class JwtTokenProvider {
private UserRepository userRepository;
private BCrypt bCrypt;
public JwtTokenProvider(UserRepository userRepository) {
this.userRepository = userRepository;
this.bCrypt = BCrypt.withDefaults();
}
public String generateToken(UserDetails userDetails) {
String authorities = userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
long now = (new Date()).getTime();
Date validity = new Date(now + 3600000);
return Jwts.builder()
.setSubject(userDetails.getUsername())
.claim("authorities", authorities)
.setIssuedAt(new Date())
.setExpiration(validity)
.signWith(SignatureAlgorithm.HS256, "secret".getBytes())
.compact();
}
public Authentication getAuthentication(HttpServletRequest request) {
String token = request.getHeader("Authorization");
if (token != null) {
Claims claims = Jwts.parser().setSigningKey("secret".getBytes()).parseClaimsJws(token).getBody();
String username = claims.getSubject();
if (username != null) {
User user = userRepository.findByUsername(username);
if (user != null) {
return new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
}
}
}
return null;
}
public boolean validateToken(HttpServletRequest request) {
String token = request.getHeader("Authorization");
if (token != null) {
Claims claims = Jwts.parser().setSigningKey("secret".getBytes()).parseClaimsJws(token).getBody();
if (claims.getExpiration().after(new Date())) {
return true;
}
}
return false;
}
}
在 src/main/java/com/example.demo.security
目录下创建 JwtTokenFilter.java
:
package com.example.demo.security;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(new JwtTokenFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
}
}
在 src/main/java/com/example.demo.controller
目录下创建 AuthController.java
:
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import com.example.demo.security.JwtTokenProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/auth")
public class AuthController {
@Autowired
private UserRepository userRepository;
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password) {
User user = userRepository.findByUsername(username);
if (user != null && bCryptPasswordEncoder.matches(password, user.getPassword())) {
return jwtTokenProvider.generateToken(user);
}
return null;
}
@PostMapping("/register")
public String register(@RequestParam String username, @RequestParam String password) {
User user = new User();
user.setUsername(username);
user.setPassword(bCryptPasswordEncoder.encode(password));
userRepository.save(user);
return jwtTokenProvider.generateToken(user);
}
}
在 src/main/java/com/example.demo.entity
目录下创建 User.java
:
package com.example.demo.entity;
import org.springframework.security.core.GrantedAuthority;
public class User extends org.springframework.security.core.userdetails.User {
public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
}
public User(String username, String password) {
super(username, password, new SimpleGrantedAuthority("ROLE_USER"));
}
}
在 src/main/java/com/example.demo.repository
目录下创建 UserRepository.java
:
package com.example.demo.repository;
import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
通过以上示例代码,实现了基本的用户认证和授权功能,以及 JWT 的使用。
这些代码示例和配置展示了如何在 Spring Boot 应用中集成 Spring Security 和 JWT 进行安全控制,确保应用程序的安全性。
共同學習,寫下你的評論
評論加載中...
作者其他優質文章