官方文档:
Spring Security :: Spring Security
功能:
身份认证(authentication)
授权(authorization)
防御常见攻击(protection against common attacks)
身份认证:
身份认证是验证谁正在访问系统资源
,判断用户是否为合法用户。认证用户的常见方式是要求用户输入用户名和密码。
授权:
用户进行身份认证后,系统会控制谁能访问哪些资源
,这个过程叫做授权。用户无法访问没有权限的资源。
防御常见攻击:
CSRF
HTTP Headers
HTTP Requests
官方代码示例:GitHub - spring-projects/spring-security-samples
项目名:security-demo
JDK:17
SpringBoot:3.2.0(依赖了Spring Security 6.2.0)
Dependencies:Spring Web、Spring Security、Thymeleaf
package com.atguigu.securitydemo.controller; @Controller public class IndexController { @GetMapping("/") public String index() { return "index"; } }
在路径resources/templates中创建index.html
Hello Security! Hello Security
Log Out
浏览器中访问:http://localhost:8080/
浏览器自动跳转到登录页面:http://localhost:8080/login
输入用户名:user
输入密码:在控制台的启动日志中查找初始的默认密码
点击"Sign in"进行登录,浏览器就跳转到了index页面
通过使用@{/logout},Thymeleaf将自动处理生成正确的URL,以适应当前的上下文路径。这样,无论应用程序部署在哪个上下文路径下,生成的URL都能正确地指向注销功能。
例如:如果我们在配置文件中添加如下内容
server.servlet.context-path=/demo
那么@{/logout}可以自动处理url为正确的相对路径
但是如果是普通的/logout,路径就会不正确
页面样式bootstrap.min.css是一个CDN地址,需要通过科学上网的方式访问
否则你的登录页会加载很久,并且看到的页面是这样的(登录按钮没有样式文件渲染,但是不影响登录功能的执行)
保护应用程序URL,要求对应用程序的任何交互进行身份验证。
程序启动时生成一个默认用户“user”。
生成一个默认的随机密码,并将此密码记录在控制台上。
生成默认的登录表单和注销页面。
提供基于表单的登录和注销流程。
对于Web请求,重定向到登录页面;对于服务请求,返回401未经授权。
处理跨站请求伪造(CSRF)攻击。
处理会话劫持攻击。
写入Strict-Transport-Security以确保HTTPS。
写入X-Content-Type-Options以处理嗅探攻击。
写入Cache Control头来保护经过身份验证的资源。
写入X-Frame-Options以处理点击劫持攻击。
ide社区版本或者专业版2022-2023
服务器地址URL 阿里云脚手架 https://start.aliyun.com/
或者原来的即可
http://start.springboot.io/
Spring Web Spring Security Thymeleaf 三个选中
然后创建
IndexController
// http://127.0.0.1:8080/
@GetMapping("/")
public String html(){
return "index.html";
}
templates/ index.html
Hello Security!
Hello Security
Log Out
Log Out2
默认情况下Spring Security将初始的用户名和密码存在了SecurityProperties类中。这个类中有一个静态内部类User,配置了默认的用户名(name = "user")和密码(password = uuid)
spring.security.user.name=user
spring.security.user.password=123
WebSecurityConfig
package com.smg.securitydemo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
@EnableWebSecurity//Spring项目总需要添加此注解,SpringBoot项目中不需要
public class WebSecurityConfig {
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser( //此行设置断点可以查看创建的user对象
User
.withDefaultPasswordEncoder()
.username("huan") //自定义用户名
.password("password") //自定义密码
.roles("USER") //自定义角色
.build()
);
return manager;
}
}
之前密码不可用
创建三个
- 创建数据库
CREATE DATABASE `security-demo`;
USE `security-demo`;
-- 创建用户表
CREATE TABLE `user`(
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(50) DEFAULT NULL ,
`password` VARCHAR(500) DEFAULT NULL,
`enabled` BOOLEAN NOT NULL
);
-- 唯一索引
CREATE UNIQUE INDEX `user_username_uindex` ON `user`(`username`);
-- 插入用户数据(密码是 "abc" )
INSERT INTO `user` (`username`, `password`, `enabled`) VALUES
('admin', '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW', TRUE),
('Helen', '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW', TRUE),
('Tom', '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW', TRUE);
数据库表并插入测试数据
mysql
mysql-connector-java
8.0.30
com.baomidou
mybatis-plus-boot-starter
3.5.4.1
org.mybatis
mybatis-spring
org.mybatis
mybatis-spring
3.0.3
org.projectlombok
lombok
1.18.10
#MySQL数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/security-demo
spring.datasource.username=root
spring.datasource.password=123456
#SQL日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
package com.smg.securitydemo.entity;
@Data
public class User {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String username;
private String password;
private Boolean enabled;
}
package com.smg.securitydemo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.smg.securitydemo.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper {
}
xml
package com.smg.securitydemo.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.smg.securitydemo.entity.User;
public interface UserService extends IService {
}
实现
package com.smg.securitydemo.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.smg.securitydemo.entity.User;
import com.smg.securitydemo.mapper.UserMapper;
import com.smg.securitydemo.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl implements UserService {
}
package com.smg.securitydemo.controller;
import com.smg.securitydemo.entity.User;
import com.smg.securitydemo.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
//localhost:8080/demo/user/list
@Resource
public UserService userService;
@GetMapping("/list")
public List getList(){
return userService.list();
}
}
登录失败
修改过后即可访问
com.baomidou
mybatis-plus-boot-starter
3.5.4.1
org.mybatis
mybatis-spring
org.mybatis
mybatis-spring
3.0.3
修改过后登录即可
package com.smg.securitydemo.config;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.smg.securitydemo.entity.User;
import com.smg.securitydemo.mapper.UserMapper;
import jakarta.annotation.Resource;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.UserDetailsManager;
import java.util.ArrayList;
import java.util.Collection;
public class DBUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService {
@Resource
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", username);
User user = userMapper.selectOne(queryWrapper);
if (user == null) {
throw new UsernameNotFoundException(username);
} else {
Collection authorities = new ArrayList<>();
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
user.getEnabled(),
true, //用户账号是否过期
true, //用户凭证是否过期
true, //用户是否未被锁定
authorities); //权限列表
}
}
@Override
public UserDetails updatePassword(UserDetails user, String newPassword) {
return null;
}
@Override
public void createUser(UserDetails user) {
}
@Override
public void updateUser(UserDetails user) {
}
@Override
public void deleteUser(String username) {
}
@Override
public void changePassword(String oldPassword, String newPassword) {
}
@Override
public boolean userExists(String username) {
return false;
}
}
修改WebSecurityConfig中的userDetailsService方法如下
@Bean
public UserDetailsService userDetailsService() {
DBUserDetailsManager manager = new DBUserDetailsManager();
return manager;
}
注意需要注释掉原来的代码
遇到这种问题
可
在
WebSecurityConfig中创建对应缺少的类
@Bean
public DBUserDetailsManager2 dbUserDetailsManager2() {
// 创建DBUserDetailsManager2的实例,这里可能需要注入其他依赖,如JdbcTemplate等
return new DBUserDetailsManager2();
}
登录数据库里的账号如admin
Swagger测试地址:http://localhost:8080/demo/doc.html
参考文章
https://docs.spring.io/spring-security/reference/features/authentication/password-storage.html
明文密码:
项目地址
security-demo: security-demo
oauth2-login-demo: oauth2-login-demo