SpringSecurity入门
本文属于SpringSecurity入门篇,后续学习过程中会持续更新
基于spring的安全框架
前言
什么是安全框架?
解决系统安全问题的框架,如果没有安全框架,我们就需要手动处理每个资源的访问控制,显得非常麻烦。使用安全框架后就可以使用配置的方式对资源进行访问控制。
常见的安全框架
- Apache Shiro:一个功能强大且易于使用的安全框架,提供了认证,授权,加密,会话管理。
- Spring Security:Spring家族中的一员,是一个能够为应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组Spring上下文配置的Bean,利用SpringIoc,DI和AOP功能,为应用系统提供声明式的安全访问控制功能,减少了企业系统安全控制编写大量重复代码工作。
1 SpringSecurity概述
SpringSecurity是一个高度自定制的安全框架,他利用SpringIoc,DI,AOP功能,为应用系统提供声明式的安全访问控制功能,减少企业系统安全控制编写大量重复代码工作。
SpringSecurity两大核心功能
- 认证:是建立一个主体的过程("主体"一般指用户,设备或可以在应用程序中执行动作的其他系统),简单说是指使用者通过账户名和密码登陆的整个过程称为认证。
- 授权:是指一个主体是否允许在应用程序中执行某个动作的过程,简单说就是给某个用户指定某个功能的访问权限。
- SpringSecurity通过过滤器实现请求拦截,实现认证,授权等操作。
2 SpringSecurity的基本使用
-
引入Springboot和SpringSecurity相关依赖
<parent> <groupId>org.springframework.boot</groupId> <version>2.7.9</version> <artifactId>spring-boot-starter-parent</artifactId> </parent> <dependencies> <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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build>
-
创建springboot启动类
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } }
-
创建Controller
@RestController @RequestMapping("/test") public class TestController { @RequestMapping("/test01") public String test01(){ return "test01"; } @RequestMapping("/test02") public String test02(){ return "test02"; } }
-
启动测试跳入如下页面
当在项目中引入SpringSecurity依赖后,整个项目就会被SpringSecurity管理起来
未认证用户无权进入系统,会自动跳转到SpringSecurity提供的登陆页面
-
如图我们需要登陆才能访问
密码在每次服务器启动时,控制台会自动生成一串字符串,如图所示
默认用户名:user
-
输入用户名和密码,进入test01页面
-
自定义SpringSecurity登陆页面
如果要使用自定义页面,需要SpringSecurity配置类中指定
-
继承
WebSecurityConfigurerAdapter
-
public class SecurityConfig extends
WebSecurityConfigurerAdapter继承的方式可能会存在安全隐患,此方式已过时
-
-
组装式定义SpringSecurity配置类
/** * SpringSecurity配置类 */ @Configuration public class SecurityConfig { }
-
-
SecurityFilterChain方法
用于拦截请求,并对请求进行处理
/** * 用于拦截请求,并对请求进行处理 * @param httpSecurity * @return SecurityFilterChain:Security过滤器链 */ @Bean @Autowired public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity .csrf().disable()//禁用跨域请求伪造的攻击 //请求配置 .authorizeRequests()//获得所有认证请求 //防止发生重定向次数过多错误,需要将login.html放行 .antMatchers("/login.html","/fail.html")//匹配指定的路径 .permitAll()//无需认证即可访问 .anyRequest()//获得任意请求 .authenticated()//必须认证才允许访问 .and() //配置form表单 .formLogin()//配置表单 .loginProcessingUrl("/login")//配置登陆处理器的url地址,该地址所对应处理类由SpringSecurity提供 .loginPage("/login.html")//配置登陆页 .failureUrl("/fail.html")//登陆失败处理页 ; return httpSecurity.build();//获得securityFilterChain的实现类对象并返回 }
-
上述拦截到页面直接无需认证让其放行,还有一种方式
若拦截页面很多,则使用下列方式
/** * 处理需要忽略的请求 * @return */ @Bean public WebSecurityCustomizer webSecurityCustomizer(){ return new WebSecurityCustomizer(){ @Override public void customize(WebSecurity web) { //配置不拦截的路径(要忽略的路径) web.ignoring().antMatchers("/login.html","/fail.html"); } }; }
3 SpringSecurity基于内置账户的实现
- 设置账户时,每个账户都必须有一个角色
- 密码必须加密,通过
BCryptPasswordEncoder
进行加密
SpringSecurity把设置的内存密码交给加密器进行加密,获得一个"盐",通过"盐"和输入的密码进行加密获得一个加密后的字符串,和设置的内存密码进行匹配,若一样登陆成功
用于处理认证和授权逻辑
- 该方法内,可以定义认证和授权相关操
- 此处功能:
设置内存账户,根据内存账户自定义用户名和密码进行登陆
`@param builder` 认证管理器的编译器对象,该对象由springSecurity自动注入
/**
*用于处理认证和授权逻辑
* 该方法内,可以定义认证和授权相关操作
* 此处功能:
* -设置内存账户,根据内存账户自定义用户名和密码进行登陆
* @param builder 认证管理器的编译器对象,该对象由springSecurity自动注入
*/
@Autowired
public void registerProvider(AuthenticationManagerBuilder builder) throws Exception {
//SpringSecurity要求密码必须加密,创建加密器对象
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
builder
.inMemoryAuthentication()//设置内存账户
.passwordEncoder(passwordEncoder)//设置密码加密器对象
.withUser("admin")//设置内置账户用户名
.password(passwordEncoder.encode("123456"))//设置内置账户密码
//SpringSecurity要求每个账户必须有一个角色,此处定义账户为设置角色
.roles("USER")//设置角色
;
}
-
加入角色后,每个资源都必须设置一个角色
在SecurityFilterChain方法设置
.antMatchers("/index.html") .hasAnyRole("USER","ADMIN") .antMatchers("/test/test01") .hasRole("ADMIN") .antMatchers("/test/test02") .hasAnyRole("USER")
-
上面设置允许角色
"USER","ADMIN"
访问资源index.html
允许角色
"USER"
访问资源test/test01
允许角色
"ADMIN"
访问资源test/test02
-
4 SpringSecurity基于数据库的实现
-
创建bean
@Data @AllArgsConstructor @NoArgsConstructor public class UserInfo implements Serializable { private Integer user_id; private String user_name; private String user_password; private String user_email; private Date user_birthday; private String user_hobbys; private Integer user_sex; private String user_address; private Integer user_status; }
-
声明
UserDetailsService
对象并注入进认证管理器@Autowired private UserDetailsService userDetailsService; @Autowired public void registerProvider(AuthenticationManagerBuilder builder) throws Exception { //SpringSecurity要求密码必须加密,创建加密器对象 PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); /** * 基于数据库 */ builder.userDetailsService(userDetailsService)//用于指定登陆的处理逻辑(认证逻辑)的对象 .passwordEncoder(passwordEncoder)//设置密码加密器 ; }
-
创建
UserDetailsServiceImpl
类并实现UserDetailsService
接口该类需要实现接口中的
loadUserByUsername
方法,该方法返回值为UserDetails
该方法作用
- 根据用户名获得用户信息
- 将用户信息认证需要的数据封装到UserDetails的实现类对象中(User)
- 将封装好的认证信息提交给SpringSecurity进行认证
UserDetails
接口中方法:-
Collection<? extends GrantedAuthority> getAuthorities();
获得权限集合 -
String getPassword();
获得账号 -
String getUsername();
获得密码 -
boolean isAccountNonExpired();
判断账户是否过期(有效) -
boolean isAccountNonLocked();
判断账户是否被冻结 -
boolean isCredentialsNonExpired();
凭证是否过期 -
boolean isEnabled();
账户是否启用
/** * 处理认证逻辑的类 * + 该类需要重写接口中的loadUserByUsername方法根据用户名获得用户对象 */ @Service public class UserDetailsServiceImpl implements UserDetailsService { /** * 1.根据用户名获得用户信息 * 2.将用户信息认证需要的数据封装到UserDetails的实现类对象中(User) * 3.将封装好的认证信息提交给SpringSecurity进行认证 * @param username * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return null; } }
-
由于要根据用户名获得用户信息则创建
UserMapper
接口@Repository public interface UserMapper { /** * * 根据用户名获得用户对象 * @param username * @return */ @Select("select * from tbl_user where user_name=#{username}") public UserInfo getUserByUsername(String username); }
-
根据三步完善
UserDetailsServiceImpl
@Resource private UserMapper userMapper; /** * 1.根据用户名获得用户信息 * 2.将用户信息认证需要的数据封装到UserDetails的实现类对象中(User) * 3.将封装好的认证信息提交给SpringSecurity进行认证 * @param username * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //根据用户名获得用户信息 UserInfo userInfo = userMapper.getUserByUsername(username); //检测用户名是否正确 if (userInfo==null){ log.info("用户名不存在"); return null; } //检测用户是否为有效用户 if (userInfo.getUser_status()==-1){ log.info("用户被冻结"); return null; } List<GrantedAuthority> authorities =new ArrayList<>(); authorities.add(new SimpleGrantedAuthority("ROLE_USER")); //将UserInfo中认证需要数据封装到User对象中(UserDetails的实现类对象) User user = new User(userInfo.getUser_name()//附加数据 ,userInfo.getUser_password()//密码 ,true//账户是否启用 ,true//账户是否过期 ,true//凭证是否过期 ,userInfo.getUser_status()==-1?false:true//账户是否被锁定 ,authorities);//账户拥有的权限 return user;//将该返回值交给SpringSecurity进行认证 }
ps:小知识点
- 前端的文本框和密码框name只能是username和password,要想使用其他name该如何解决
在配置from表单里设置
.formLogin()//配置表单
.loginProcessingUrl("/login")//配置登陆处理器的url地址,该地址所对应处理类由SpringSecurity提供
.loginPage("/login.html")//配置登陆页
.failureUrl("/fail.html")//登陆失败处理页
.usernameParameter("myusername")//前端文本框的name
.passwordParameter("mypassword")//前端密码框的name
- 学习来自于西安加中实训
文章来源地址https://www.toymoban.com/news/detail-605843.html文章来源:https://www.toymoban.com/news/detail-605843.html
到了这里,关于SpringSecurity入门(超级无敌认真好用,万字收藏篇!!!!)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!