一:介绍
1.定义
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框 架。Spring Security 主要实现了Authentication(认证,解决who are you? ) 和 Access Control(访问控制,也就是what are you allowed to do?,也称为Authorization)。Spring Security在架构上将认证与授权分离,并提供了扩展点。
2.解释
认证 :用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时系统要求验证用户的身份信息,身份合法方可继续访问,不合法则拒绝访问。常见的用户身份认证方式有:用户名密码登录,二维码登录,手机短信登录,指纹认证等方式。
授权: 授权是用户认证通过根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访问,没有权限则拒绝访问。
3.springsecurity与shrio
优点:
1:Spring Security基于Spring开发,项目中如果使用Spring作为基础,配合Spring Security做权限更加方便,而Shiro需要和Spring进行整合开发
2:Spring Security功能比Shiro更加丰富些,例如安全防护
3:Spring Security社区资源比Shiro丰富
缺点:
1:Shiro的配置和使用比较简单,Spring Security上手复杂
2:Shiro依赖性低,不需要任何框架和容器,可以独立运行,而Spring Security依赖于Spring容器
二:使用
1.用户身份认证
1.1 默认方式:
引用pom
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
编写contoller:
package com.jun.security.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/admin") public class AdminController { @GetMapping("/demo") public String demo() { return "spring security demo"; } }
引入Spring Security之后 ,访问 API 接口时,需要首先进行登录,才能进行访问。 测试 http://localhost:8080/admin/demo ,会跳转到
需要登录,默认用户名:user,密码可以查看控制台日志获取
Using generated security password: fc858c10-51e3-4055-b653-ef9a141861da
登录之后跳转到:
1.2 基于yml配置文件设置用户名和密码:
spring: security: user: name: caojun password: 123456 roles: ADMIN
可以使用配置的用户名和密码配置。
原理: 默认情况下,UserDetailsServiceAutoConfiguration自动化配置类,会创建一个内存级别的 InMemoryUserDetailsManager对象,提供认证的用户信息。
添加 spring.security.user 配置项,UserDetailsServiceAutoConfiguration 会基于配置的信息在内存中创建一个用户User。
未添加 spring.security.user 配置项,UserDetailsServiceAutoConfiguration 会自动在内存中创建一个用户名为 user,密码为 UUID 随机的用户 User。
1.3 基于UserDetailService接口:
先上加密文件:
package com.jun.security.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration public class WebSecurityConfig { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
然后写userDetailService:
package com.jun.security.service; 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.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCrypt; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class UserDetailsServiceImpl implements UserDetailsService { @Resource private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { String password = passwordEncoder.encode("123456"); // 支持的加密方式可以通过PasswordEncoderFactories查看 return User .withUsername("caojunjun") .password(password) .authorities("admin").build(); } }
1.4 WebSecurityConfigureAdapter配置类
创建 WebSecurityConfig配置类,继承 WebSecurityConfigurerAdapter抽象类,实现 Spring Security 在 Web 场景下的自定义配置
package com.jun.security.config; import com.jun.security.service.UserDetailsServiceImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import javax.annotation.Resource; @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Resource private UserDetailsServiceImpl userDetailsService; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 1.直接引入userDetailsService auth.userDetailsService(userDetailsService); } }
1.5 WebSecurityConfigureAdapter配置类,内存篇日志方式
package com.jun.security.config; import com.jun.security.service.UserDetailsServiceImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import javax.annotation.Resource; @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Resource private UserDetailsServiceImpl userDetailsService; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 1.直接引入userDetailsService // auth.userDetailsService(userDetailsService); // 2.使用内存配置 auth.inMemoryAuthentication() .withUser("jun1").password(passwordEncoder().encode("112233")).roles("admin") .and() .withUser("jun2").password(passwordEncoder().encode("223344")).roles("user"); } }
1.6 数据库访问
配置:
spring: datasource: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC username: root password: 123456 hikari: minimum-idle: 5 idle-timeout: 600000 maximum-pool-size: 10 auto-commit: true pool-name: MyHikariCP max-lifetime: 1800000 connection-timeout: 30000 connection-test-query: SELECT 1
userDeatilsService
package com.jun.security.service; import com.jun.security.mapper.PermissionMapper; import com.jun.security.mapper.UserMapper; import com.jun.security.model.Permission; import com.jun.security.model.User; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCrypt; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; @Service public class UserDetailsServiceImpl implements UserDetailsService { @Resource private PasswordEncoder passwordEncoder; @Resource private UserMapper userMapper; @Autowired private PermissionMapper permissionMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { System.out.println("自定义登录逻辑"); //从mysql查询用户 User user = getByUsername(username); List<GrantedAuthority> authorities = new ArrayList<>(); if (user != null) { List<Permission> permissions = permissionMapper.selectByUserId(user.getId()); //设置权限 permissions.forEach(permission -> { if (permission != null && !StringUtils.isEmpty(permission.getEnname())) { GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getEnname()); authorities.add(grantedAuthority); } }); // 封装成UserDetails的实现类 return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities); } else { throw new UsernameNotFoundException("用户名不存在"); } } public User getByUsername(String username) { return userMapper.getByUsername(username); } }
2.自定义登陆页面
配置:
认证和授权,可以使用默认成功的页面,也可以使用url进行重定向【这里写一下重定向的接口】
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Resource private UserDetailsServiceImpl userDetailsService; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } // @Override // protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 1.直接引入userDetailsService // auth.userDetailsService(userDetailsService); // 2.使用内存配置 // auth.inMemoryAuthentication() // .withUser("jun1").password(passwordEncoder().encode("112233")).roles("admin") // .and() // .withUser("jun2").password(passwordEncoder().encode("223344")).roles("user"); // } @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() //表单提交 .loginPage("/login.html") //自定义登录页面 .loginProcessingUrl("/user/login") //登录访问路径,必须和表单提交接口一样 // .defaultSuccessUrl("/main.html") //认证成功之后跳转的路径 .successForwardUrl("/tomain") .failureForwardUrl("/toerror"); http.authorizeRequests() //设置哪些路径可以直接访问,不需要认证 .antMatchers("/user/login", "/login.html").permitAll() .anyRequest().authenticated() //需要认证 .and().csrf().disable(); //关闭csrf防护 } }
重定向接口:
@Controller public class LoginController { @RequestMapping("/tomain") public String tomain() { return "redirect:/main.html"; } @RequestMapping("/toerror") public String toerror() { return "redirect:/error.html"; } @RequestMapping("/showLogin") public String showLogin() { return "login"; } }
登陆页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/user/login" method="post"> 用户名:<input type="text" name="username"/><br/> 密码: <input type="password" name="password"/><br/> 记住我:<input type="checkbox" name="remember-me" value="true"/><br/> <input type="submit" value="提交"/> </form> </body> </html>
3.自定义处理器
成功处理器:
package com.jun.security.handler; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author Fox */ public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler { private String redirectUrl; public MyAuthenticationSuccessHandler(String redirectUrl) { this.redirectUrl = redirectUrl; } @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { response.sendRedirect(redirectUrl); } }
失败处理器:
package com.jun.security.handler; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author Fox */ public class MyAuthenticationFailHandler implements AuthenticationFailureHandler { private String redirectUrl; public MyAuthenticationFailHandler(String redirectUrl) { this.redirectUrl = redirectUrl; } @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { response.sendRedirect(redirectUrl); } }
配置:
/** * 自定义处理器 */ @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() //表单提交 .loginPage("/login.html") //自定义登录页面 .loginProcessingUrl("/user/login") //登录访问路径,必须和表单提交接口一样 .successHandler(new MyAuthenticationSuccessHandler("/main.html")) .failureHandler(new MyAuthenticationFailHandler("/error.html")); http.authorizeRequests() //设置哪些路径可以直接访问,不需要认证 .antMatchers("/user/login", "/login.html").permitAll() .anyRequest().authenticated() //需要认证 .and().csrf().disable(); //关闭csrf防护 }
4.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)