当前位置: 首页 > backend >正文

SpringBoot -- 集成Spring Security (二)

一、Spring Security 简介

Spring Security 是 Spring 家族中的安全框架,主要提供:

  • 认证(Authentication):用户是谁(登录验证)。

  • 授权(Authorization):用户能做什么(访问权限控制)。

  • 防护(Protection):常见攻击防护,如 CSRF、会话劫持等。

在 Spring Boot 项目中,Spring Security 常见的使用方式是:

  • 配置 安全拦截规则(哪些 URL 需要登录、哪些需要权限)。

  • 定义 用户认证信息来源(内存 / 数据库 / 第三方服务)。

  • 配置 密码加密登录 / 登出页面


二、核心代码与配置

1. 视图配置(代替 SpringMVC.xml)

@Configuration // 相当于 springmvc.xml
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {// 访问根路径 “/” 时,重定向到 /login-viewregistry.addViewController("/").setViewName("redirect:/login-view");// 配置登录页映射:/login-view -> login.jspregistry.addViewController("/login-view").setViewName("login");}
}

2. Spring Security 核心配置

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
// 开启基于方法的权限控制(@PreAuthorize、@Secured)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {//定义用户信息服务(查询用户信息),使用了SpringDataUserDetailsService类 就不用使用这个方法
/*@Beanpublic UserDetailsService userDetailsService(){InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());return manager;}
*///密码编码器(不对密码进行加密,密码是明文存储 & 明文校验。)【不推荐使用】/*@Beanpublic PasswordEncoder passwordEncoder(){return NoOpPasswordEncoder.getInstance();}*/// 密码编码器(推荐使用 BCrypt,避免明文存储)@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}// 安全拦截配置@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable() // 关闭 CSRF(前后端分离常关闭,表单需开启).authorizeRequests()
//               .antMatchers("/r/r1").hasAuthority("p1")//基于url的方式授权,但是一般使用基于方法的方式授权即一般在controller的方法上加@PreAuthorize("hasAuthority('p1')")
//              .antMatchers("/r/r2").hasAuthority("p2")//基于url的方式授权,但是一般使用基于方法的方式授权即一般在controller的方法上加@PreAuthorize("hasAuthority('p2')").antMatchers("/r/**").authenticated() // 所有 /r/** 需要认证.anyRequest().permitAll() // 其他请求直接放行.and().formLogin() // 开启表单登录.loginPage("/login-view") //用户没有登录的话要跳转到 /login-view 这个路径 ,如果不指定,Spring Security会生成一个默认的login登录页面.loginProcessingUrl("/login") // 登录表单提交地址【与jsp中定义的<form action="login" method="post">的action要相同】.successForwardUrl("/login-success") // 登录成功跳转.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // 按需创建 session.and().logout().logoutUrl("/logout") // 登出请求.logoutSuccessUrl("/login-view?logout"); // 登出成功后跳转}
}

3. 登录 & 资源访问控制器

@RestController
public class LoginController {// 登录成功后跳转到这里@RequestMapping(value = "/login-success", produces = {"text/plain;charset=UTF-8"})public String loginSuccess() {return getUsername() + " 登录成功";}// 资源1:需要 p1 权限@GetMapping(value = "/r/r1", produces = {"text/plain;charset=UTF-8"})@PreAuthorize("hasAuthority('p1')")public String r1() {return getUsername() + " 访问资源1";}// 资源2:需要 p2 权限@GetMapping(value = "/r/r2", produces = {"text/plain;charset=UTF-8"})@PreAuthorize("hasAuthority('p2')")public String r2() {return getUsername() + " 访问资源2";}// 获取当前登录用户private String getUsername() {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();Object principal = authentication.getPrincipal();if (principal instanceof UserDetails) {return ((UserDetails) principal).getUsername();}return principal.toString();}
}

4. 数据库访问层

@Repository
public class UserDao {@AutowiredJdbcTemplate jdbcTemplate;// 根据账号查询用户信息public UserDto getUserByUsername(String username) {String sql = "select id,username,password,fullname,mobile from t_user where username = ?";List<UserDto> list = jdbcTemplate.query(sql,new Object[]{username},new BeanPropertyRowMapper<>(UserDto.class));return (list != null && list.size() == 1) ? list.get(0) : null;}// 根据用户id查询权限public List<String> findPermissionsByUserId(String userId) {String sql = "SELECT * FROM t_permission WHERE id IN(" +"SELECT permission_id FROM t_role_permission WHERE role_id IN(" +"  SELECT role_id FROM t_user_role WHERE user_id = ? ))";List<PermissionDto> list = jdbcTemplate.query(sql,new Object[]{userId},new BeanPropertyRowMapper<>(PermissionDto.class));List<String> permissions = new ArrayList<>();list.forEach(c -> permissions.add(c.getCode()));return permissions;}
}

5. 用户与权限 DTO

@Data
public class UserDto {private String id;private String username;private String password;private String fullname;private String mobile;
}@Data
public class PermissionDto {private String id;private String code;        // 权限标识private String description; // 描述private String url;         // 资源路径
}

6. 自定义 UserDetailsService(认证逻辑)

//这个就是4.2 认证(Authentication)流程中的图解的第五步
//我们实现UserDetailsService接口,重写了loadUserByUsername方法,返回我们定义的UserDetails 
@Service
public class SpringDataUserDetailsService implements UserDetailsService {@AutowiredUserDao userDao;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 查询用户信息UserDto userDto = userDao.getUserByUsername(username);if (userDto == null) {throw new UsernameNotFoundException("用户不存在");}// 查询用户权限List<String> permissions = userDao.findPermissionsByUserId(userDto.getId());String[] permissionArray = permissions.toArray(new String[0]);// 构造 Spring Security Userreturn User.withUsername(userDto.getUsername()).password(userDto.getPassword()).authorities(permissionArray).build();}
}

7. 启动类

@SpringBootApplication
public class SecuritySpringBootApp {public static void main(String[] args) {SpringApplication.run(SecuritySpringBootApp.class, args);}
}

8. 配置文件(application.properties)

server.port=8080
server.servlet.context-path=/security-springboot
spring.application.name=security-springbootspring.mvc.view.prefix=/WEB-INF/view/
spring.mvc.view.suffix=.jspspring.datasource.url=jdbc:mysql://localhost:3306/user_db
spring.datasource.username=root
spring.datasource.password=mysql
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

9. 登录页面(login.jsp)

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="utf-8" %>
<html>
<head><title>用户登录</title>
</head>
<body>
<form action="login" method="post">用户名:<input type="text" name="username"><br>密码:<input type="password" name="password"><br><input type="submit" value="登录">
</form>
</body>
</html>

10. Maven 依赖

<dependencies><!-- Web 依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Security 依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!-- JDBC + MySQL --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- JSP 视图支持 --><dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-jasper</artifactId></dependency><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId></dependency>
</dependencies>

三、总结

  1. 登录认证:由 Spring Security 接管,使用 UserDetailsService 从数据库加载用户信息。

  2. 权限控制:支持 URL 拦截(antMatchers)和方法级拦截(@PreAuthorize)。

  3. 密码加密:使用 BCryptPasswordEncoder,避免明文存储。

  4. 前后端交互:表单方式(JSP)提交到 /login,由 Security 自动处理。

http://www.xdnf.cn/news/18447.html

相关文章:

  • CTFSHOW | 其他篇题解(二)web417 - web437
  • LeetCode第55题 - 跳跃游戏
  • 学习游戏制作记录(合成表UI和技能树的UI)8.22
  • SpringBoot项目创建的五种方式
  • 53 C++ 现代C++编程艺术2-枚举和枚举类
  • C++ unistd.h库文件介绍(文件与目录操作, 进程管理, 系统环境访问, 底层I/O操作, 系统休眠/执行控制)
  • Linux服务测试
  • 【链表 - LeetCode】24. 两两交换链表中的节点
  • 深入理解 Java IO 流 —— 从入门到实战
  • 排序(数据结构)
  • nanoGPT 部署
  • JUC之Fork/Join
  • EP4CE40F23I7N Altera FPGA Cyclone IV E
  • LLM实践系列:利用LLM重构数据科学流程
  • shell脚本第二阶段-----选择结构
  • 企业设备系统选型:功能适配度分析
  • Vue 插槽(Slots)全解析1
  • B树,B+树,B*树
  • 文件包含的学习笔记
  • 嵌入式Linux学习 -- 网络1
  • 深度学习——神经网络
  • canvas绘制图片等比缩放
  • Vue2+Vue3前端开发_Day6
  • Linux笔记8——shell编程基础-2
  • 网络实践——Socket编程UDP
  • 视频拼接融合技术:打造全景视界的革命性产品
  • API模型与接口弃用指南:历史、替代方案及开发者应对策略
  • `git mv` 重命名 Git 仓库中的文件夹
  • 多人编程新方式:cpolar 让 OpenHands 远程开发更轻松
  • 20250822在Ubuntu24.04.2下指定以太网卡的IP地址