1.Spring Security是什么
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
2.为什么使用Spring Security
可以帮助进行认证和授权:
认证:就是用户登录的时候,检验用户的信息是否正确
授权:就是字面的意思,就像是每个用户又那些权限(vip1,vip2)
3.如何使用Spring Security
3.1引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
3.2启动项目 ,控制台会出现登录密码。账号未user
可以通过配置文件来修改账号密码:
也可以通过配置类来修改账号密码:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder pe = new BCryptPasswordEncoder();
String encode = pe.encode("123456");
auth.inMemoryAuthentication()
.passwordEncoder(pe)
.withUser("gjc") //设置用户名
.password(encode).roles("USER");//设置密码以及角色
}
}
4.自定义表单登录
如果不直接使用框架自带的html页面的话,就需要我们配置页面比如:使用jsp,或者使用thymeleaf模板引擎
4.1自定义登录页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>登录</title>
<style>
</style>
</head>
<body>
<form action="/userlogin" method="post">
用户名:<input type="text" name="myname"> <!--name=myname注意后台需要配置这个名字-->
<br/>
密码:<input type="password" name="mypwd"> <!--name=mypwd注意后台需要配置这个名字-->
<br/>
<input type="submit" value="login">
</form>
</body>
</html>
4.2修改配置类
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 这是配置自定义的信息
http
.formLogin().loginPage("/login.html") //配置为自定义的登录页面
.usernameParameter("myname") //设置页面form表单的用户名参数
.passwordParameter("mypwd") //设置页面form表单的密码参数
.loginProcessingUrl("/userlogin") //登录路径和前台form登录接口路径保持一致
// .defaultSuccessUrl("/")//登录成功跳转路径
.and().authorizeRequests().antMatchers("/","userlogin","/login.html").permitAll()//设置哪些路径不需要拦截
.anyRequest().authenticated()//除去不需要认证的路径的其它路径都需要认证
// .and().exceptionHandling().accessDeniedPage("")//自定义没有权限的页面
.and().csrf().disable(); //关闭csrf的保护
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder pe = new BCryptPasswordEncoder();
String encode = pe.encode("123456");
auth.inMemoryAuthentication()
.passwordEncoder(pe)
.withUser("gjc")
.password(encode).roles("USER");
}
}
5.通过数据库账号密码登录
如果需要根据数据库的账号密码进行登录,就需要替换掉原本的UserDetailsService,因为之前实在内存中查找账号密码,现在需要从数据库中查找。
5.1准备工作
依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--spring security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--mp-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
</dependencies>
mysql连接配置:properties
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.username=root
spring.datasource.druid.password=root
spring.datasource.druid.url=jdbc:mysql://localhost:3306/qy168?serverTimezone=Asia/Shanghai
#配置mybatis plus驼峰关闭
mybatis-plus.configuration.map-underscore-to-camel-case=false
#控制台打印sql语句日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
实体类:
@Data
@TableName("tbl_user")
public class TblUser {
@TableId(type = IdType.AUTO)
private long id;
private String username;
private String password;
private String headImg;
private String phone;
private String realname;
}
mybatis plus的mapper层和server层这里就不列举了,相信大家都会了
5.2替换UserDetailsService
替换UserDetailsService,重写其中的方法,从数据库中查询用户信息
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//查询用户信息
LambdaQueryWrapper<TblUser> query = new LambdaQueryWrapper<>();
query.eq(TblUser::getUsername,username);
TblUser tblUser = userService.getOne(query);
//如果没有查询到用户信息就抛出异常
if (Objects.isNull(tblUser)){
throw new RuntimeException("用户名或密码错误");
}
//TODO 查询对应的权限信息
//因为现在用户还没有权限模拟以下权限
// 查询用户的权限信息并将其转换为GrantedAuthority对象列表
List<GrantedAuthority> authorities = new ArrayList<>();
// 例如,假设用户有ROLE_USER角色
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
//将数据封装为UserDetails对象
//因为这个User实现了UserDetails,所以我们可以使用这个User实体类
User user = new User(tblUser.getUsername(), tblUser.getPassword(), authorities);
return user;
}
}
测试:发现报错了
因为这里密码默认采用了一种PasswordEncoder密码校验的东西,如果要让密码是明文存储,需要在密码前加{noop}
真正开发肯定是不会用这种方式的,密码也不会明文存储。我们一般使用的SpringSecurity为我们提供的BCryptPasswordEncoder.
使用非常简单,只需要把BCryptPasswordEncoder注入到bean容器就行
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
测试:
@Test
void text(){
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encode = passwordEncoder.encode("123456");//对123456进行加密
String encode1 = passwordEncoder.encode("123456");//对123456进行加密
System.out.println(encode);
System.out.println(encode1);
//盐相同密文是不同的
//比较
boolean matches = passwordEncoder.matches("123456", "$2a$10$4IfzCQBjoxfmBGLg.SlnfOKwEBkuXDYBoEpU.8kUmBYF5WO.5zb42");
boolean matches1 = passwordEncoder.matches("123456", "$2a$10$FDltmVxzybG9HNx6e1jBu.NvBLHM4QD0lmZVV98igMQ4TgOCOet.e");
System.out.println(matches);
System.out.println(matches1);
}
运行结果:
那么我们只需要修改数据库的密码为加密之后的密码
再进行登录就可以了,这块在注册时就需要使用BcryptPasswordEncoder对密码加密存储到数据库中
6.前后端分离
6.1分析
登录:
1.需要自定义登录接口 -- 调用ProviderManager的方法进行认证 如果认证通过生成jwt -- 把用户信息存入redis中
2.自定义UserDetailsService -- 在这个实现类中查询数据库
校验:
1.定义jwt认证过滤器 -- 获取token -- 解析token获取其中的userid -- 从redis中获取用户信息 -- 存入SecurityContextHolder
6.2准备工作:
依赖:在之前依赖中新增
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--jwt依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
redis配置: