系列文章目录
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
Spring Security之认证
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 系列文章目录
- 前言
- 一、什么是Spring Security
- 二、Spring Security之认证
- 环境准备
- 数据库认证
- 自定义认证逻辑
- PasswordEncoder
- 自定义登录页面
- 突破CSRF防护
- 会话管理
- 认证成功后的处理
- 认证失败后的处理
- 退出登录
- 退出成功处理器
- Remember Me
- 总结
前言
提示:这里可以添加本文要记录的大概内容:
当涉及到构建安全的 Web 应用程序时,Spring Security 是一个广受欢迎且功能强大的框架。它提供了一系列的特性和功能,用于保护应用程序免受未经授权的访问。
在这篇博客中,我们将深入探讨 Spring Security 的认证部分。认证是安全体系的第一道防线,它确保只有经过身份验证的用户才能访问受保护的资源。
我们将从 Spring Security 的基础知识开始,了解它的核心概念和组件。然后,我们将逐步介绍如何使用 Spring Security 实现常见的认证方式,如用户名和密码登录。
无论你是 Spring Security 的新手还是有一定经验的开发者,这篇博客都将为你提供一个深入了解 Spring Security 认证的机会。通过掌握这些知识,你将能够构建更安全、可靠的 Web 应用程序。
让我们一起开启 Spring Security 认证之旅,保护我们的应用程序免受未经授权的访问!
提示:以下是本篇文章正文内容,下面案例可供参考
一、什么是Spring Security
Spring Security 是一个用于保护 Spring 应用程序安全性的框架。它提供了一套全面的安全特性,可以帮助开发人员轻松地构建安全的应用程序。Spring Security 的核心目标是提供一种易于使用、灵活且强大的方式来保护应用程序的安全性。
Spring Security 的主要功能包括身份验证、授权、密码加密、跨域安全性等。它支持多种认证方式,如基本认证、摘要认证、JWT 认证等。同时,Spring Security 还提供了强大的授权功能,允许开发人员根据用户角色和权限来限制对特定资源的访问。
总的来说,Spring Security 是一个非常有用的框架,可以帮助开发人员轻松地构建安全的应用程序。
二、Spring Security之认证
认证就是判断用户身份是否合法,如果合法就可以继续访问,不合法就拒绝访问
环境准备
1.准备一个名为mysecurity的Mysql数据库
2.创建SpringBoot项目,添加依赖
<!-- SpringMVC -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Thymeleaf-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--Spring Security-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Mysql驱动 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope>
</dependency>
<!-- MyBatisPlus -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.0</version>
</dependency>
<!-- lombok -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
<!-- junit -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
3.为SpringBoot项目编写配置文件
server:port: 80#日志格式
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n'# 数据源
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql:///mysecurity?serverTimezone=UTCusername: rootpassword: root
4.在template文件夹编写项目主页面main.html
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>主页面</title>
</head>
<body>
<h1>主页面</h1>
</body>
</html>
5.编写访问页面控制器
@Controller
public class PageController {@RequestMapping("/{page}")public String showPage(@PathVariable String page){return page;}
}
数据库认证
在实际的项目中,用户通过输入用户名和密码,待验证通过后即登录成功,而用户信息大多保存在数据库中。而用户身份验证的这个过程,我们称之为认证逻辑。
1.准备数据库数据,用户信息数据都是保存在该数据库中
CREATE TABLE `users` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(255),`password` varchar(255) ,`phone` varchar(255) ,PRIMARY KEY (`id`)
);INSERT INTO `users` VALUES (1, 'baizhan', 'baizhan', '13812345678');
INSERT INTO `users` VALUES (2, 'sxt', 'sxt', '13812345678');
2.编写用户实体类
@Data
public class Users {private Integer id;private String username;private String password;private String phone;
}
3.编写dao接口
public interface UsersMapper extends BaseMapper<Users> {
}
4.在 SpringBoot启动类中添加 @MapperScan 注解,扫描Mapper文件夹
@SpringBootApplication
@MapperScan("com.itbaizhan.myspringsecurity.mapper")
public class MysecurityApplication {public static void main(String[] args) {SpringApplication.run(MysecurityApplication.class, args);}
}
自定义认证逻辑
在实际项目中,认证逻辑是需要自定义控制的。将 UserDetailsService 接口的实现类放入Spring容器即可自定义认证逻辑。UserDetailsService 的实现类必须重写 loadUserByUsername 方法,该方法定义了具体的认证逻辑,参数 username 是前端传来的用户名,我们需要根据传来的用户名查询到该用户(一般是从数据库查询),并将查询到的用户封装成一个UserDetails对象,该对象是Spring Security提供的用户对象,包含用户名、密码、权限。Spring Security会根据UserDetails对象中的密码和客户端提供密码进行比较。相同则认证通过,不相同则认证失败。
5.创建UserDetailsService的实现类,编写自定义认证逻辑
@Service
public class MyUserDetailsService implements UserDetailsService {@Autowiredprivate UsersMapper usersMapper;// 自定义认证逻辑@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 1.构造查询条件QueryWrapper<Users> wrapper = new QueryWrapper<Users>().eq("username", username);// 2.查询用户Users users = usersMapper.selectOne(wrapper);// 3.封装为UserDetails对象UserDetails userDetails = User.withUsername(users.getUsername()).password(users.getPassword()).authorities("admin").build();// 4.返回封装好的UserDetails对象return userDetails;}
}
PasswordEncoder
在实际的项目开发中,我们往数据库的密码不会是明文密码,而是经过我们加密后的密码。当用户传入的明文密码后,SpringSecurity使用密码解析器将明文密码加密成密文密码,然后再将数据库中的密文密码进行比对,匹配成功则通过,反之不通过。
创建SecurityConfig配置类,添加SpringSecurity密码解析器(PasswordEncoder)
// Security配置类
@Configuration
public class SecurityConfig {//密码编码器@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}
自定义登录页面
SpringSecurity给我们提供了登录界面,但是再实际的项目中,登录页面都是用的自己的,这样更能突出项目特色,所以我们要自定义登录界面。
1.编写登录界面
2.在Spring Security配置类自定义登录页面
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{//Spring Security配置@Overrideprotected void configure(HttpSecurity http) throws Exception {// 自定义表单登录http.formLogin() .loginPage("/login.html") // 自定义登录页面.usernameParameter("username") // 表单中的用户名项.passwordParameter("password") // 表单中的密码项.loginProcessingUrl("/login") // 登录路径,表单向该路径提交,提交后自动执行UserDetailsService的方法.successForwardUrl("/main") //登录成功后跳转的路径.failureForwardUrl("/fail"); //登录失败后跳转的路径// 需要认证的资源http.authorizeRequests().antMatchers("/login.html").permitAll() //登录页不需要认证.anyRequest().authenticated(); //其余所有请求都需要认证//关闭csrf防护http.csrf().disable();}@Overridepublic void configure(WebSecurity web) throws Exception {// 静态资源放行web.ignoring().antMatchers("/css/**");}
}
突破CSRF防护
CSRF:跨站请求伪造,通过伪造用户请求访问受信任的站点从而进行非法请求访问,是一种攻击手段。SpringSecurity默认开启CSRF防护,这就限制了除了GET请求以外的大多数请求。我们可以通过关闭CSRF防护来解决问题,但是这就不够安全了。CSRF为了保证不是其他第三方网站访问,要求访问时携带参数名为_csrf值为令牌,令牌在服务端产生,如果携带的令牌和服务端的令牌匹配成功,则正常访问。
<form class="form" action="/login" method="post"><!-- 在表单中添加令牌隐藏域 --><input type="hidden" th:value="${_csrf.token}" name="_csrf" th:if="${_csrf}"/><input type="text" placeholder="用户名" name="username"><input type="password" placeholder="密码" name="password"><button type="submit">登录</button>
</form>
会话管理
SpringSecurity提供了会话管理功能,它将用户信息保存再会话中,我们可以通过SecurityContext对象中获取用户信息。
@RestController
public class MyController {// 获取当前登录用户名@RequestMapping("/users/username")public String getUsername(){// 1.获取会话对象SecurityContext context = SecurityContextHolder.getContext();// 2.获取认证对象Authentication authentication = context.getAuthentication();// 3.获取登录用户信息UserDetails userDetails = (UserDetails) authentication.getPrincipal();return userDetails.getUsername();}
}
认证成功后的处理
如果在认证成功后,需要处理一些自定义的逻辑,可以使用登陆成功处理器。
1.自定义登录成功处理器
public class MyLoginSuccessHandler implements AuthenticationSuccessHandler {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {// 拿到登录用户的信息UserDetails userDetails = (UserDetails)authentication.getPrincipal();System.out.println("用户名:"+userDetails.getUsername());System.out.println("一些操作...");// 重定向到主页response.sendRedirect("/main");}
}
2.配置登录成功处理器
http.formLogin() // 使用表单登录.loginPage("/login.html") // 自定义登录页面.usernameParameter("username") // 表单中的用户名项.passwordParameter("password") // 表单中的密码项.loginProcessingUrl("/login") // 登录路径,表单向该路径提交,提交后自动执行UserDetailsService的方法// .successForwardUrl("/main") //登录成功后跳转的路径.successHandler(new MyLoginSuccessHandler()) //登录成功处理器.failureForwardUrl("/fail"); //登录失败后跳转的路径
认证失败后的处理
如果在认证成功后,需要处理一些自定义的逻辑,可以使用登陆失败处理器。
1.自定义登录失败处理器
public class MyLoginFailureHandler implements AuthenticationFailureHandler {@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {System.out.println("记录失败日志...");response.sendRedirect("/fail");}
}
2.配置登录失败处理器
http.formLogin() // 使用表单登录.loginPage("/login.html") // 自定义登录页面.usernameParameter("username") // 表单中的用户名项.passwordParameter("password") // 表单中的密码项.loginProcessingUrl("/login") // 登录路径,表单向该路径提交,提交后自动执行UserDetailsService的方法// .successForwardUrl("/main") //登录成功后跳转的路径.successHandler(new MyLoginSuccessHandler()) //登录成功处理器// .failureForwardUrl("/fail") //登录失败后跳转的路径.failureHandler(new MyLoginFailureHandler()); //登录失败处理器// 需要认证的资源
http.authorizeRequests().antMatchers("/login.html").permitAll() // 登录页不需要认证.antMatchers("/fail").permitAll() // 失败页不需要认证.anyRequest().authenticated(); //其余所有请求都需要认证
退出登录
当用户退出后,需要清楚认证状态、销毁HttpSession对象,跳转登录界面
1.配置退出登录的路径和退出后跳转的路径
// 退出登录配置
http.logout().logoutUrl("/logout") // 退出登录路径.logoutSuccessUrl("/login.html") // 退出登录后跳转的路径.clearAuthentication(true) //清除认证状态,默认为true.invalidateHttpSession(true); // 销毁HttpSession对象,默认为true
2.在网页中添加退出登录超链接
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>主页面</title>
</head>
<body>
<h1>主页面</h1>
<a href="/logout">退出登录</a>
</body>
</html>
退出成功处理器
我们也可以自定义退出成功处理器,在退出后清理一些数据,写法如下:
1.自定义退出成功处理器
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {System.out.println("清除一些数据...");response.sendRedirect("/login.html");}
}
2.配置退出成功处理器
// 退出登录配置
http.logout().logoutUrl("/logout") // 退出登录路径// .logoutSuccessUrl("/login.html") // 退出登录后跳转的路径.clearAuthentication(true) //清除认证状态,默认为true.invalidateHttpSession(true) // 销毁HttpSession对象,默认为 true.logoutSuccessHandler(new MyLogoutSuccessHandler()); //自定义退出成功处理器
Remember Me
SpringSecurity提供了“记住我”功能,当使用"记住我"功能登陆后,SpringSecurity会生成一个令牌,令牌一方面保存在数据库里,另一方面生成一个叫remember-me的Cookie保存到客户端。之后客户端访问项目时自动携带令牌,不登录即可完成认证。
1.编写“记住我”配置类
@Configuration
public class RememberMeConfig {@Autowiredprivate DataSource dataSource;// 令牌Repository@Beanpublic PersistentTokenRepository getPersistentTokenRepository() {// 为Spring Security自带的令牌控制器设置数据源JdbcTokenRepositoryImpl jdbcTokenRepositoryImpl = new JdbcTokenRepositoryImpl();jdbcTokenRepositoryImpl.setDataSource(dataSource);//自动建表,第一次启动时需要,第二次启动时注释掉
// jdbcTokenRepositoryImpl.setCreateTableOnStartup(true);return jdbcTokenRepositoryImpl;}
}
2.修改Security配置类
// 记住我配置
http.rememberMe().userDetailsService(userDetailsService)//登录逻辑交给哪个对象.tokenRepository(repository) //持久层对象.tokenValiditySeconds(30); //保存时间,单位:秒
3.在登录页面添加“记住我”复选框
<form class="form" action="/login" method="post"><input type="text" placeholder="用户名" name="username"><input type="password" placeholder="密码" name="password"><input type="checkbox" name="remember-me" value="true"/>记住我</br><button type="submit">登录</button>
</form>
总结
提示:这里对文章进行总结:
总的来说,Spring Security 为我们提供了一个强大而灵活的框架来保护应用程序的安全性。通过理解和应用所学的知识,我们可以构建更安全、可靠的 Web 应用程序。
希望这篇博客对你有所帮助,让你对 Spring Security 的认证有了更深入的了解。如果你有任何进一步的问题或想要深入探讨其他安全相关主题,请随时留言!