Springboot 系列 (4) - 在 Spring Boot 项目里使用 Spring Security、Druid 和 MyBatis
Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。Spring Boot 给 Spring Security 提供了自动化配置方案 (spring-boot-starter-security),可以零配置使用 Spring Security。
Druid 是阿里巴巴推出的一款开源的高性能数据源产品,Druid 支持所有 JDBC 兼容的数据库,包括 Oracle、MySQL、SQL Server 和 H2 等等。Druid 不仅结合了 C3P0、DBCP 和 PROXOOL 等数据源产品的优点,同时还加入了强大的监控功能。通过 Druid 的监控功能,可以实时观察数据库连接池和 SQL 的运行情况,帮助用户及时排查出系统中存在的问题。
Druid GitHub: https://github.com/alibaba/druid
MyBatis 是一个半自动化的 ORM 框架,所谓半自动化是指 MyBatis 只支持将数据库查出的数据映射到 POJO 实体类上,而实体到数据库的映射则需要我们自己编写 SQL 语句实现,相较于Hibernate 这种完全自动化的框架,Mybatis 更加灵活,我们可以根据自身的需求编写 SQL 语句来实现复杂的数据库操作。
MyBatis 也开发了一套基于 Spring Boot 模式的 starter:mybatis-spring-boot-starter。
MyBatis GitHub: https://github.com/mybatis
1. 开发环境
Windows版本:Windows 10 Home (20H2)
IntelliJ IDEA (https://www.jetbrains.com/idea/download/):Community Edition for Windows 2020.1.4
Apache Maven (https://maven.apache.org/):3.8.1
注:Spring 开发环境的搭建,可以参考 “ Spring基础知识(1)- Spring简介、Spring体系结构和开发环境配置 ”。
2. 创建 Spring Boot 基础项目
项目实例名称:SpringbootExample04
Spring Boot 版本:2.6.6
创建步骤:
(1) 创建 Maven 项目实例 SpringbootExample04;
(2) Spring Boot Web 配置;
(3) 导入 Thymeleaf 依赖包;
(4) 配置静态资源(jQuery、Bootstrap、Images);
具体操作请参考 “Springboot 系列 (2) - 在 Spring Boot 项目里使用 Thymeleaf、JQuery+Bootstrap 和国际化” 里的项目实例 SpringbootExample02,文末包含如何使用 spring-boot-maven-plugin 插件运行打包的内容。
SpringbootExample04 和 SpringbootExample02 相比,SpringbootExample04 不包含 Thymeleaf 模版文件(templates/*.htm l)和国际化。
3. 配置 Spring security
1) 修改 pom.xml,导入 Spring security 依赖包
1 <project ... > 2 ... 3 <dependencies> 4 ... 5 6 <!-- Spring security --> 7 <dependency> 8 <groupId>org.springframework.boot</groupId> 9 <artifactId>spring-boot-starter-security</artifactId> 10 </dependency> 11 12 ... 13 </dependencies> 14 15 ... 16 </project>
在IDE中项目列表 -> SpringbootExample04 -> 点击鼠标右键 -> Maven -> Reload Project
2) 修改 src/main/resources/application.properties 文件,添加如下配置
# security
spring.security.user.name=admin
spring.security.user.password=123456
spring.security.user.roles=admin
运行并访问 http://localhost:9090/test,自动跳转到 http://localhost:9090/login (Spring security 的默认页面),输入上面的用户名和密码登录,登录后跳转到 http://localhost:9090/test。
注:如果不在 application.properties 里配置 admin, 默认用户名是 user,密码在项目启动时输出在控制台,格式如下:
Using generated security password: 61c29bfd-0f2b-4a35-a27c-c569f9b6f02d
This generated password is for development use only. Your security configuration must be updated before running your application in production.
配置 admin 后,默认用户 user 将失效。
3) 创建 src/main/resources/templates/common.html 文件
1 <div th:fragment="header(var)"> 2 <meta charset="UTF-8"> 3 <title th:text="${var}">Title</title> 4 <link rel="stylesheet" th:href="@{/lib/bootstrap-4.2.1-dist/css/bootstrap.min.css}" href="/lib/bootstrap-4.2.1-dist/css/bootstrap.min.css"> 5 <script language="javascript" th:src="@{/lib/jquery/jquery-3.6.0.min.js}" src="/lib/jquery/jquery-3.6.0.min.js"></script> 6 <script language="javascript" th:src="@{/lib/bootstrap-4.2.1-dist/js/bootstrap.min.js}" src="/lib/bootstrap-4.2.1-dist/js/bootstrap.min.js"></script> 7 </div> 8 9 <div th:fragment="content-header" class="container" id="content-header-id"> 10 <nav class="navbar navbar-light bg-light"> 11 <a class="navbar-brand" href="#"> 12 <img th:src="@{/images/bootstrap-solid.svg}" src="/images/bootstrap-solid.svg" width="30" height="30" class="d-inline-block align-top" alt=""> 13 Thymeleaf Demo 14 </a> 15 16 <a class="nav-link" th:href="@{/logout}" th:if="${session.loginUser} != null">Logout</a> 17 </nav> 18 </div> 19 20 <div th:fragment="content-footer(var)" class="container" id="content-footer-id"> 21 <p th:text="${var}">Content Footer</p> 22 </div>
4) 创建 src/main/resources/templates/login.html 文件
1 <!DOCTYPE html> 2 <html lang="en" xmlns:th="http://www.thymeleaf.org"> 3 <head th:include="common::header(var='Login')"> 4 </head> 5 <body> 6 <div th:replace="common::content-header"></div> 7 8 <div class="container" id="content" th:style="'min-height: 480px;'"> 9 <h4>Login</h4> 10 11 <p> </p> 12 <div class="alert alert-info" role="alert" th:text="${message}" th:if ="${message} != null"></div> 13 14 <form method="POST" action="/login/post"> 15 <div class="form-group"> 16 <label for="username">Username</label> 17 <input type="text" class="form-control" id="username" name="username" value="admin" /> 18 </div> 19 <div class="form-group"> 20 <label for="password">Password</label> 21 <input type="password" class="form-control" id="password" name="password" value="123456" /> 22 </div> 23 <button type="submit" class="btn btn-primary">Submit</button> 24 </form> 25 26 </div> 27 28 <div th:replace="common::content-footer(var='Copyright © 2020')"></div> 29 30 <script type="text/javascript"> 31 $(document).ready(function(){ 32 console.log("jQuery is running"); 33 }); 34 </script> 35 </body> 36 </html>
5) 创建 src/main/resources/templates/home.html 文件
1 <!DOCTYPE html> 2 <html lang="en" xmlns:th="http://www.thymeleaf.org"> 3 <head th:include="common::header(var='Home')"> 4 </head> 5 <body> 6 <div th:replace="common::content-header"></div> 7 8 <div class="container" id="content" th:style="'min-height: 480px;'"> 9 <h4>Home Page</h4> 10 11 <p> </p> 12 <div class="alert alert-info" role="alert" th:text="${message}" th:if ="${message} != null"></div> 13 </div> 14 15 <div th:replace="common::content-footer(var='Copyright © 2020')"></div> 16 17 <script type="text/javascript"> 18 $(document).ready(function(){ 19 console.log("jQuery is running"); 20 }); 21 </script> 22 </body> 23 </html>
6) 修改 src/main/java/com/example/controller/IndexController.java 文件
1 package com.example.controller; 2 3 import org.springframework.ui.Model; 4 import org.springframework.stereotype.Controller; 5 import org.springframework.web.bind.annotation.RequestMapping; 6 import org.springframework.web.bind.annotation.ResponseBody; 7 8 @Controller 9 public class IndexController { 10 @ResponseBody 11 @RequestMapping("/test") 12 public String test() { 13 return "Test Page"; 14 } 15 16 @RequestMapping("/home") 17 public String home(Model model) { 18 model.addAttribute("message", "Spring Boot Login Demo"); 19 return "home"; 20 } 21 22 @RequestMapping("/login") 23 public String login() { 24 return "login"; 25 } 26 }
7) 创建 src/main/java/com/example/config/WebSecurityConfig.java 文件
1 package com.example.config; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 6 import org.springframework.security.config.annotation.web.builders.HttpSecurity; 7 import org.springframework.security.core.userdetails.UserDetailsService; 8 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 9 10 @Configuration 11 public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 12 @Autowired 13 private UserDetailsService userDetailsService; 14 15 @Override 16 protected void configure(AuthenticationManagerBuilder auth) throws Exception { 17 auth.userDetailsService(userDetailsService); 18 } 19 20 @Override 21 protected void configure(HttpSecurity http) throws Exception { 22 // 配置认证 23 http.authorizeRequests() 24 .antMatchers("/lib/**").permitAll() 25 .antMatchers("/images/**").permitAll() 26 .antMatchers("/error").permitAll() 27 .antMatchers("/**/*.html").permitAll() 28 .anyRequest().authenticated() 29 30 .and() 31 .formLogin() 32 .loginPage("/login") // 自定义登录页面 33 .loginProcessingUrl("/login/post") // 登录访问路径 34 .defaultSuccessUrl("/home").permitAll() // 登陆成功之后跳转地址 35 36 .and() 37 .csrf().disable(); // 关闭 csrf 保护功能,默认是开启的 38 39 } 40 41 }
运行并访问 http://localhost:9090/home,自动跳转到 http://localhost:9090/login (Spring security 的默认页面),输入上面的用户名和密码,登录后跳转到 http://localhost:9090/home。
4. 配置 Druid
1) 修改 pom.xml,导入 MariaDB、JDBC、Druid 依赖包
1 <project ... > 2 ... 3 <dependencies> 4 ... 5 6 <!-- MariaDB --> 7 <dependency> 8 <groupId>org.mariadb.jdbc</groupId> 9 <artifactId>mariadb-java-client</artifactId> 10 </dependency> 11 <!-- JDBC --> 12 <dependency> 13 <groupId>org.springframework.boot</groupId> 14 <artifactId>spring-boot-starter-data-jdbc</artifactId> 15 </dependency> 16 <!-- Druid --> 17 <dependency> 18 <groupId>com.alibaba</groupId> 19 <artifactId>druid</artifactId> 20 <version>1.2.8</version> 21 </dependency> 22 23 ... 24 </dependencies> 25 26 ... 27 </project>
在IDE中项目列表 -> SpringbootExample04 -> 点击鼠标右键 -> Maven -> Reload Project
2) 创建 src/main/java/com/example/config/DruidDataSourceConfig.java 文件
1 package com.example.config; 2 3 import javax.sql.DataSource; 4 import java.sql.SQLException; 5 6 import com.alibaba.druid.pool.DruidDataSource; 7 import org.springframework.boot.context.properties.ConfigurationProperties; 8 import org.springframework.context.annotation.Bean; 9 import org.springframework.context.annotation.Configuration; 10 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 11 12 @Configuration 13 public class DruidDataSourceConfig implements WebMvcConfigurer { 14 15 @ConfigurationProperties("spring.datasource") 16 @Bean 17 public DataSource dataSource() throws SQLException { 18 DruidDataSource druidDataSource = new DruidDataSource(); 19 return druidDataSource; 20 } 21 }
3) 修改 src/main/resources/application.properties 文件,添加如下代码:
1 # 数据源连接信息 2 spring.datasource.driver-class-name=org.mariadb.jdbc.Driver 3 spring.datasource.url=jdbc:mysql://localhost:3306/springboot_example 4 spring.datasource.username=root 5 spring.datasource.password=123456
4) 修改 src/main/java/com/example/controller/IndexController.java 文件
1 package com.example.controller; 2 3 import java.sql.SQLException; 4 import javax.sql.DataSource; 5 import org.springframework.beans.factory.annotation.Autowired; 6 7 import org.springframework.ui.Model; 8 import org.springframework.stereotype.Controller; 9 import org.springframework.web.bind.annotation.RequestMapping; 10 import org.springframework.web.bind.annotation.ResponseBody; 11 12 @Controller 13 public class IndexController { 14 @Autowired 15 DataSource dataSource; 16 17 ... 18 19 @ResponseBody 20 @RequestMapping("/druid") 21 public String druid() throws SQLException { 22 String str = "Druid Page<br><br>"; 23 str += "DataSource:" + dataSource.getClass() + "<br>"; 24 str += "DataSource Connection:" + dataSource.getConnection() + "<br>"; 25 return str; 26 } 27 }
运行并访问 http://localhost:9090/druid,登录后,页面显示类似如下内容就表示 Druid 设置成功。
Druid Page
DataSource:class com.alibaba.druid.pool.DruidDataSource
DataSource Connection:org.mariadb.jdbc.MariaDbConnection@3fc45e93
注:如何开启 Druid 内置监控页面,请参考 “Springboot基础知识(15)- 整合 Druid 数据源” 的整合 Druid 数据源。
5. 配置数据库(MariaDB)
1) XAMPP for Windows
https://www.apachefriends.org/download.html
本文安装版本 7.4.25,用到 XAMPP 的如下功能:
+ Apache 2.4.51
+ MariaDB 10.4.21 (MySQL的分支)
+ PHP 7.4.25 (VC15 X86 64bit thread safe) + PEAR
+ phpMyAdmin 5.1.1
2) 创建数据库 springboot_example 和 user 表,SQL 脚本如下
1 CREATE TABLE `user` ( 2 `id` int(11) NOT NULL, 3 `username` varchar(50) NOT NULL, 4 `password` varchar(255) DEFAULT NULL, 5 `age` int(11) DEFAULT NULL, 6 `createtime` timestamp NULL DEFAULT NULL 7 ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 8 9 ALTER TABLE `user` ADD PRIMARY KEY (`id`); 10 11 ALTER TABLE `user` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; 12 13 INSERT INTO `user` (`id`, `username`, `password`, `age`, `createtime`) VALUES 14 (1, 'admin', 'admin', 21, '2020-01-01 01:01:01'),(2, 'test', 'test', 28, '2020-02-02 02:02:02');
3)创建 src/main/java/com/example/entity/User.java 文件
1 package com.example.entity; 2 3 import java.util.Date; 4 5 public class User { 6 private Integer id; 7 private String username; 8 private String password; 9 private Integer age; 10 private Date createtime; 11 12 public User() { 13 14 } 15 16 public Integer getId() { 17 return id; 18 } 19 public void setId(Integer id) { 20 this.id = id; 21 } 22 23 public String getUsername() { 24 return username; 25 } 26 public void setName(String username) { 27 this.username = username; 28 } 29 30 public String getPassword() { 31 return password; 32 } 33 public void setPassword(String password) { 34 this.password = password; 35 } 36 37 public Integer getAge() { 38 return age; 39 } 40 public void setAge(Integer age) { 41 this.age = age; 42 } 43 44 public Date getCreatetime() { 45 return createtime; 46 } 47 public void setCreatetime(Date createtime) { 48 this.createtime = createtime; 49 } 50 51 @Override 52 public String toString() { 53 return "User {" + 54 "username = " + username + 55 ", password = " + password + 56 ", age = " + age + 57 ", createtime = " + createtime + 58 '}'; 59 } 60 }
6. 配置 MyBatis
1)修改 pom.xml,导入 MyBatis 依赖包
1 <project ... > 2 ... 3 <dependencies> 4 ... 5 6 <!-- MyBatis --> 7 <dependency> 8 <groupId>org.mybatis.spring.boot</groupId> 9 <artifactId>mybatis-spring-boot-starter</artifactId> 10 <version>2.2.0</version> 11 </dependency> 12 13 ... 14 </dependencies> 15 16 ... 17 </project>
在IDE中项目列表 -> SpringbootExample04 -> 点击鼠标右键 -> Maven -> Reload Project
2) 修改 src/main/resources/application.properties 文件,添加如下代码:
# mybatis
mybatis.type-aliases-package=com.example
mybatis.configuration.map-underscore-to-camel-case=true
3)创建 src/main/java/com/example/mapper/UserMapper.java 文件
1 package com.example.mapper; 2 3 import org.apache.ibatis.annotations.Mapper; 4 import org.apache.ibatis.annotations.Select; 5 import org.apache.ibatis.annotations.Param; 6 7 import com.example.entity.User; 8 9 @Mapper 10 public interface UserMapper { 11 12 @Select("SELECT * FROM user WHERE username = #{username}") 13 User getUserByName(@Param("username") String username); 14 15 }
4) 修改 src/main/java/com/example/controller/IndexController.java 文件
1 package com.example.controller; 2 3 import java.sql.SQLException; 4 import javax.sql.DataSource; 5 import org.springframework.beans.factory.annotation.Autowired; 6 7 import org.springframework.ui.Model; 8 import org.springframework.stereotype.Controller; 9 import org.springframework.web.bind.annotation.RequestMapping; 10 import org.springframework.web.bind.annotation.ResponseBody; 11 12 import com.example.entity.User; 13 import com.example.mapper.UserMapper; 14 15 @Controller 16 public class IndexController { 17 @Autowired 18 DataSource dataSource; 19 @Autowired 20 private UserMapper userMapper; 21 22 ... 23 24 @ResponseBody 25 @RequestMapping("/mybatis") 26 public String mybatis() throws SQLException { 27 User user = userMapper.getUserByName("test"); 28 String str = "MyBatis Page<br><br>"; 29 str += user + "<br>"; 30 return str; 31 } 32 }
运行并访问 http://localhost:9090/mybatis,登录后,页面显示类似如下内容就表示 MyBatis 设置成功。
MyBatis Page
User {username = test, password = test, age = 21, createtime = Wed Jan 01 01:01:01 CST 2020}
7. 把 MyBatis 用户数据整合到 Security 的用户验证
1) 创建 src/main/java/com/example/service/MyUserDetailsService.java 文件
1 package com.example.service; 2 3 import java.util.List; 4 import java.util.ArrayList; 5 6 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.security.core.userdetails.User; 8 import org.springframework.security.core.userdetails.UserDetails; 9 import org.springframework.security.core.userdetails.UserDetailsService; 10 import org.springframework.security.core.userdetails.UsernameNotFoundException; 11 import org.springframework.security.core.GrantedAuthority; 12 13 import com.example.mapper.UserMapper; 14 15 public class MyUserDetailsService implements UserDetailsService { 16 @Autowired 17 private UserMapper userMapper; 18 19 @Override 20 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 21 22 com.example.entity.User user = userMapper.getUserByName(username); 23 24 List<GrantedAuthority> authorities = new ArrayList<>(); 25 26 User userDetails= new User(user.getUsername(), user.getPassword(), authorities); 27 return userDetails; 28 } 29 }
2) 修改 src/main/java/com/example/config/WebSecurityConfig.java 文件
1 package com.example.config; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 6 import org.springframework.security.config.annotation.web.builders.HttpSecurity; 7 //import org.springframework.security.core.userdetails.UserDetailsService; 8 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 9 10 import org.springframework.context.annotation.Bean; 11 import org.springframework.security.crypto.password.NoOpPasswordEncoder; 12 import org.springframework.security.crypto.password.PasswordEncoder; 13 import com.example.service.MyUserDetailsService; 14 15 @Configuration 16 public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 17 @Autowired 18 //private UserDetailsService userDetailsService; 19 private MyUserDetailsService userDetailsService; 20 21 // 数据库用明文密码,所以需要 NoOpPasswordEncoder 编码器 22 @Bean 23 public PasswordEncoder passwordEncoder(){ 24 return NoOpPasswordEncoder.getInstance(); 25 } 26 27 @Override 28 protected void configure(AuthenticationManagerBuilder auth) throws Exception { 29 //auth.userDetailsService(userDetailsService); 30 auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); 31 } 32 33 ... 34 35 }
运行并访问 http://localhost:9090/test,自动跳转到 http://localhost:9090/login。注意:这里要输入数据库里的用户名和密码(admin/admin 或 test/test),登录成功后显示 http://localhost:9090/test 页面。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 上周热点回顾(2.17-2.23)
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)