介绍
spring security 6.3.1 是一个重要的版本更新,它建立在 spring security 6 的核心现代化架构之上,提供了更简洁的配置、更好的默认安全性以及对最新安全标准的支持。以下是一个 spring security 6.3.1 的使用指南,涵盖核心概念、配置和常见任务:
核心理念:
- lambda dsl (推荐): spring security 6 开始大力推广基于 lambda 的 dsl (领域特定语言) 配置方式,它更简洁、类型安全,并且避免了链式调用
.and()
的繁琐。这是当前推荐的配置方式。 - 组件化: 安全配置被分解为更小、更专注的组件 (
securityfilterchain
,userdetailsservice
,passwordencoder
,authenticationprovider
等),提高了灵活性和可测试性。 - 默认安全性增强: 默认配置提供了更强的安全防护(例如,默认启用 csrf 保护,更安全的会话管理)。
- servlet 和 reactive 分离: api 清晰区分了 servlet (传统 web) 和 reactive (响应式 webflux) 应用的支持。
- oauth2 和 oidc 现代化: 对 oauth 2.1 和 openid connect 1.0 提供了更现代、更符合规范的支持。
基础配置 (servlet 应用 - 使用 lambda dsl)
添加依赖 (maven):
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-security</artifactid> <version>3.2.0</version> <!-- spring boot 3.2.x 通常包含 spring security 6.3.x --> </dependency>
核心配置类 (securityconfig
)
import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; import org.springframework.security.config.annotation.web.builders.httpsecurity; import org.springframework.security.config.annotation.web.configuration.enablewebsecurity; import org.springframework.security.core.userdetails.user; import org.springframework.security.core.userdetails.userdetails; import org.springframework.security.core.userdetails.userdetailsservice; import org.springframework.security.crypto.bcrypt.bcryptpasswordencoder; import org.springframework.security.crypto.password.passwordencoder; import org.springframework.security.provisioning.inmemoryuserdetailsmanager; import org.springframework.security.web.securityfilterchain; @configuration @enablewebsecurity // 启用 spring security web 支持 public class securityconfig { // 核心配置:定义安全过滤器链 (securityfilterchain) @bean public securityfilterchain securityfilterchain(httpsecurity http) throws exception { http // 授权配置 (核心) .authorizehttprequests(authorize -> authorize .requestmatchers("/", "/home", "/public/**", "/css/**", "/js/**", "/images/**").permitall() // 允许所有人访问 .requestmatchers("/user/**").hasrole("user") // 需要 user 角色 .requestmatchers("/admin/**").hasrole("admin") // 需要 admin 角色 .anyrequest().authenticated() // 其他所有请求都需要认证 ) // 表单登录配置 .formlogin(formlogin -> formlogin //.loginpage("/login") // 自定义登录页路径。不指定则使用默认登录页 .permitall() // 允许所有人访问登录页 .defaultsuccessurl("/dashboard") // 登录成功后的默认跳转页 ) // 退出登录配置 .logout(logout -> logout .logouturl("/logout") // 退出登录的 url (默认也是 /logout) .logoutsuccessurl("/login?logout") // 退出成功后的跳转页 .permitall() ) // 异常处理 (如访问拒绝) .exceptionhandling(exceptionhandling -> exceptionhandling .accessdeniedpage("/access-denied") // 自定义访问拒绝页 ) // 启用 http basic 认证 (可选,常用于 api) //.httpbasic(customizer.withdefaults()) // 启用 csrf 保护 (默认启用,对于 api 可能需要禁用) //.csrf(csrf -> csrf.disable()); // 谨慎禁用! return http.build(); } // 配置内存用户存储 (仅用于演示/测试,生产环境用数据库) @bean public userdetailsservice userdetailsservice(passwordencoder passwordencoder) { userdetails user = user.builder() .username("user") .password(passwordencoder.encode("password")) // 必须加密! .roles("user") .build(); userdetails admin = user.builder() .username("admin") .password(passwordencoder.encode("adminpass")) .roles("user", "admin") .build(); return new inmemoryuserdetailsmanager(user, admin); } // 配置密码编码器 (强制要求,不能使用明文!) @bean public passwordencoder passwordencoder() { return new bcryptpasswordencoder(); // 推荐 bcrypt // 其他选项: pbkdf2passwordencoder, scryptpasswordencoder, argon2passwordencoder, delegatingpasswordencoder } }
关键配置详解(lambda dsl)
authorizehttprequests(authorize -> authorize ...)
: 定义 url 的访问规则。.requestmatchers(...).permitall()
: 匹配指定路径模式,允许所有访问(包括匿名)。.requestmatchers(...).hasrole("role")
/.hasauthority("authority")
: 匹配路径,要求用户拥有指定角色或权限。注意hasrole
会自动添加role_
前缀,所以配置里写user
对应数据库/用户详情中的role_user
。hasauthority
则要求完全匹配字符串。.anyrequest().authenticated()
: 匹配所有未在前面规则中匹配的请求,要求用户已认证(登录)。.anyrequest().denyall()
: 匹配所有未在前面规则中匹配的请求,拒绝所有访问。
formlogin(formlogin -> formlogin ...)
: 配置基于表单的登录。.loginpage("/login")
: 指定自定义登录页面的 url。不指定则使用spring security 默认提供一个简单登录页。.loginprocessingurl("/login")
: 指定处理登录表单提交的 url(通常与登录页表单的action
一致)。.defaultsuccessurl("/dashboard", true)
: 登录成功后重定向的 url。第二个参数true
表示总是重定向到此 url,忽略登录前访问的受保护页面。.usernameparameter("username")
/.passwordparameter("password")
: 自定义表单中用户名和密码字段的名称。.failureurl("/login?error")
: 登录失败后重定向的 url。.permitall()
: 允许所有人访问登录相关的端点。- 默认登录页的注意事项
- 自动生成位置
- 当访问受保护资源时,会自动重定向到
/login
- 无需创建控制器或视图,spring security 自动处理
- 当访问受保护资源时,会自动重定向到
- 样式限制
- 默认页面样式非常基础(无 css)
- 不适合生产环境,建议用于原型开发或内部工具
- 功能包含
- 包含 csrf 令牌(默认启用)
- 显示错误消息(认证失败时)
- 提供 “remember me” 选项(需额外启用)
- 启用 “remember me”
.rememberme(remember -> remember .key("uniqueandsecretkey") .tokenvalidityseconds(86400) // 1天 )
logout(logout -> logout ...)
: 配置退出登录。.logouturl("/logout")
: 触发退出登录的 url (默认是/logout
)。.logoutsuccessurl("/login?logout")
: 退出成功后的重定向 url。.invalidatehttpsession(true)
: 是否使 http session 失效 (默认true
)。.deletecookies("jsessionid")
: 指定退出时要删除的 cookie 名称。.permitall()
: 允许所有人访问退出端点 (通常不需要认证即可退出)。
exceptionhandling(exceptionhandling -> exceptionhandling ...)
: 处理安全异常。.accessdeniedpage("/access-denied")
: 当已认证用户访问其没有权限的资源时,重定向到指定页面。.accessdeniedhandler(accessdeniedhandler)
: 使用自定义的accessdeniedhandler
处理访问拒绝。.authenticationentrypoint(authenticationentrypoint)
: 当未认证用户尝试访问受保护资源时,使用自定义的authenticationentrypoint
处理 (如重定向到登录页或返回 401)。
httpbasic(customizer.withdefaults())
: 启用 http basic 认证。常用于 api。csrf(csrf -> csrf.disable())
: 谨慎使用! 禁用 csrf 保护。通常只在创建无状态 api (如 jwt) 时禁用。对于有 session 的 web 应用,强烈建议保持启用。
用户认证 (进阶)
userdetailsservice
: 核心接口,用于根据用户名加载用户信息 (userdetails
)。你需要实现它来定义如何从你的数据源(数据库、ldap、外部服务等)加载用户。
@service public class jpauserdetailsservice implements userdetailsservice { private final userrepository userrepository; public jpauserdetailsservice(userrepository userrepository) { this.userrepository = userrepository; } @override public userdetails loaduserbyusername(string username) throws usernamenotfoundexception { return userrepository.findbyusername(username) .map(securityuser::new) // 将你的领域用户转换为 userdetails .orelsethrow(() -> new usernamenotfoundexception("user not found: " + username)); } }
- 确保你的
securityuser
类实现了userdetails
接口,或直接使用 spring security 的user
类(或其构建器)来包装你的领域用户信息(用户名、密码、权限集合、是否启用/过期/锁定等)。 passwordencoder
: 必须使用。负责密码的加密存储和验证。推荐bcryptpasswordencoder
。
@bean public passwordencoder passwordencoder() { return passwordencoderfactories.createdelegatingpasswordencoder(); // 推荐:支持多种编码,默认 bcrypt // 或 return new bcryptpasswordencoder(); }
authenticationprovider
(可选): 如果你想自定义认证逻辑(例如,除了用户名密码还验证其他信息),可以实现authenticationprovider
接口并注册它。通常daoauthenticationprovider
(使用userdetailsservice
和passwordencoder
) 已足够。
方法级安全(method security)
- 启用: 在配置类上添加
@enablemethodsecurity
。
@configuration @enablemethodsecurity public class methodsecurityconfig { // ... }
- 使用注解:
@preauthorize("hasrole('admin')")
/@preauthorize("hasauthority('delete_user')")
: 在方法执行前进行权限检查。支持 spel 表达式,非常灵活。@postauthorize
: 在方法执行后进行权限检查(例如,检查返回值)。@secured("role_admin")
: 较旧的注解,只支持简单的角色列表,功能不如@preauthorize
强大。@rolesallowed("user")
: jsr-250 标准注解,类似于@secured
。
@service public class userservice { @preauthorize("hasrole('admin') or #id == authentication.principal.id") // spel 示例:允许管理员或用户自己访问 public user getuserbyid(long id) { // ... } @preauthorize("hasauthority('user_delete')") public void deleteuser(long id) { // ... } }
oauth2 / oidc 资源服务器(保护 api)
spring security 6 对 oauth2 资源服务器的配置进行了显著简化。
- 添加依赖:
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-oauth2-resource-server</artifactid> </dependency>
- 配置 jwt 解码器 (最常见): 在
application.properties
/application.yml
中配置颁发者 uri (issuer-uri) 或 jwk set uri (jwk-set-uri)。spring boot 会自动配置jwtdecoder
。
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://your-auth-server.com/realms/your-realm # 或者 # spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://your-auth-server.com/jwks
- 配置资源服务器安全过滤器链:
@bean securityfilterchain resourceserversecurityfilterchain(httpsecurity http) throws exception { http .securitymatcher("/api/**") // 只对 /api/** 路径应用此安全配置 .authorizehttprequests(authorize -> authorize .anyrequest().authenticated() // api 端点都需要认证 ) .oauth2resourceserver(oauth2 -> oauth2 .jwt(customizer.withdefaults()) // 使用 jwt 作为承载令牌格式 // .opaquetoken(customizer.withdefaults()) // 使用不透明令牌 ); return http.build(); }
spring security 会自动使用配置的 jwtdecoder
验证 jwt 签名、过期时间等,并提取声明 (claims) 构建 jwtauthenticationtoken
。
- 访问令牌中的信息: 在控制器或服务中,可以通过
@authenticationprincipal
注入jwt
对象或jwtauthenticationtoken
@getmapping("/api/userinfo") public map<string, object> getuserinfo(@authenticationprincipal jwt jwt) { return map.of( "username", jwt.getsubject(), "email", jwt.getclaimasstring("email"), "scopes", jwt.getclaimasstringlist("scope") ); }
最佳实践与注意事项
- 始终使用
passwordencoder
: 绝对不要存储明文密码。 - 最小权限原则: 只授予用户完成工作所必需的权限。
- 保持依赖更新: spring security 会修复漏洞,及时更新到最新稳定版。
- 理解 csrf: 对于有状态 web 应用(使用 session 和 cookie),保持 csrf 保护启用。对于纯无状态 api(如只使用 jwt 的 api),可以禁用 csrf (
csrf.disable()
)。 - https: 在生产环境中始终使用 https。
- 安全头: spring security 默认设置了许多安全相关的 http 响应头 (如
x-content-type-options
,x-frame-options
,strict-transport-security
,content-security-policy
等)。理解并根据需要配置它们 (http.headers(headers -> headers ...)
)。 - 日志与监控: 记录安全相关事件(登录成功/失败、访问拒绝等)并进行监控。
- 避免
websecurityconfigureradapter
: 在 spring security 5.7 中已弃用,在 6.x 中移除。始终使用securityfilterchain
bean 配置。 - 谨慎使用
permitall()
: 确保只对真正公开的资源使用它。 - 测试: 编写单元测试和集成测试来验证你的安全配置是否按预期工作。使用
@withmockuser
,@withuserdetails
,@withanonymoususer
等注解进行模拟用户测试。
参考
- 官方文档: 这是最权威、最新的信息来源。spring security 6.3.x 文档(查看对应版本的 reference 和 servlet 部分)
- spring boot security auto-configuration(了解 boot 为你自动配置了什么)
- spring security samples: github 上的官方示例
到此这篇关于spring security6.3.x使用指南的文章就介绍到这了,更多相关spring security6.3.x使用内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论