Spring Security 入门

Spring Security 入门

一、介绍

SpringSecurity是一个安全框架,可以帮助我们减少重复代码的同时实现对资源的可控访问。

1、认证

认证及判断用户是否登录,有些资源是用户必须要登录才能使用的,如点赞收藏等。

2、授权

用户登录后,不同的资源可能被不同程度的管控,比如说VIP视频只有VIP用户才可以观看。这是对用户更细粒度的划分。

总结:认证就好比你想要进入公司,你必须是员工才能进入。进入公司后,你只可以前往你所在的部门,而其它部门你无权访问,这便是授权。

二、认证功能实现

环境搭建:
1、创建数据库mysecurity
2、创建SpringBoot项目
2.1添加依赖


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>3.1.5version>
        <relativePath/> 
    parent>
    <groupId>com.examplegroupId>
    <artifactId>securitydemoartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>securitydemoname>
    <description>securitydemodescription>
    <url/>
    <licenses>
        <license/>
    licenses>
    <developers>
        <developer/>
    developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    scm>
    <properties>
        <java.version>17java.version>
    properties>
    <dependencies>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-thymeleafartifactId>
        dependency>
        <dependency>
            <groupId>org.thymeleaf.extrasgroupId>
            <artifactId>thymeleaf-extras-springsecurity6artifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-securityartifactId>
        dependency>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.49version>
            <scope>runtimescope>
        dependency>
        
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>3.5.3version>
        dependency>
        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>

project>

2.2编写配置文件

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.jdbc.Driver
    url: jdbc:mysql:///mysecurity?serverTimezone=UTC
    username: root
    password: 123456

2.3编写主页面main.html

DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>主页面title>
head>
<body>
<h1>主页面h1>
body>
html>

2.4编写控制器方法

@Controller
public class PageController {
  @RequestMapping("/{page}")
  public String showPage(@PathVariable String page){
    return page;
   }
}

2.5访问localhost/main 后跳转至登陆页面,证明搭建成功

1、UserDetailsService介绍

UserDetailsService用于处理自定义认证逻辑,它可以帮助我们检查用户密码是否正确,以及封装用户信息等操作。

执行流程:用户提交表单,执行重写的loadUserByUsername方法,根据传入的用户名查找数据库(需程序员手动编写),如果用户为空,抛出异常,非空则封装权限,将用户名密码及权限封装为UserDetails对象,框架会将UserDetails中的密码项与表单中的密码自动进行比对。

@Service
// 实现UserDetailsService接口,重写loadUserByUsername方法
public class MyUserDetailsService implements UserDetailsService {
  @Autowired
  private UsersMapper usersMapper;

  // 自定义认证逻辑
  @Override
  public 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;
   }
}

2、PasswordEncoder对象介绍

PasswordEncoder该对象是SpringSecurity框架用来实现对密码的加密与解密操作的密码解析器,可以保证用户数据在网络传输过程中的安全性。

@SpringBootTest
public class PasswordEncoderTest {
  @Test
  public void testBCryptPasswordEncoder(){
    //创建解析器
    PasswordEncoder encoder = new BCryptPasswordEncoder();


    //密码加密
    String password = encoder.encode("test");
    System.out.println("加密后:"+password);


    //密码校验
    /**
     * 参数1:明文密码
     * 参数2:加密密码
     * 返回值:是否校验成功
     */
    boolean result = encoder.matches("test","$2a$10$/MImcrpDO21HAP2amayhme8j2SM0YM50/WO8YBH.NC1hEGGSU9ByO");
    System.out.println(result);
   }
}

3、自定义登陆页面

我们使用SpringSecurity后,如果我们未登录访问某些资源,会自动跳转到SpringSecurity默认的登陆页面,但是往往我们需要使用自己的登陆页面。

我们可以在配置类中自定义登陆页面

@Configuration
@EnableWebSecurity
public class SecurityConfig {

  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    // 自定义表单登录
    http.formLogin(form -> {
      form.loginPage("/login.html") // 自定义登录页面
           .usernameParameter("username") // 表单中的用户名项
           .passwordParameter("password") // 表单中的密码项
           .loginProcessingUrl("/login") //前端调用接口login后,会自动执行UserDetailsService的自定义认证逻辑方法
           .successForwardUrl("/index.html") //登录成功后跳转的路径
           .failureForwardUrl("/error.html"); //登录失败后跳转的路径
     });


    // 需要认证的资源
    http.authorizeHttpRequests(resp -> {
      resp.requestMatchers("/login.html","/error.html").permitAll(); // 不需要认证的资源
      resp.requestMatchers("/static/**").permitAll(); // 静态资源不需要认证
      resp.anyRequest().authenticated();//其余所有请求都需要认证
     });


    // 关闭csrf防护
    http.csrf(csrf ->{
      csrf.disable();
     });
    return http.build();
   }
}

4、自定义处理器

当登录成功/失败或者退出登录时,我们玩玩需要做一些事情,比如说登录成功后需要给前端返回一个token令牌,退出登录时需要清除session会话等。

例:这里以登录成功为例
4.1 自定义登录成功处理器

public class MyLoginSuccessHandler implements AuthenticationSuccessHandler {
  @Override
  public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
    // 拿到当前用户信息
    UserDetails userDetails = (UserDetails)authentication.getPrincipal();
    //执行操作。。。
    
   }
}

4.2 修改配置类

// 自定义表单登录
http.formLogin(form -> {
  form.loginPage("/login.html") 
     .usernameParameter("username") 
     .passwordParameter("password") 
     .loginProcessingUrl("/login") 
     .successHandler(new MyLoginSuccessHandler()) // 登录成功处理器,登录成功会执行该方法
     .failureForwardUrl("/error.html");
});

5、退出登录

http.logout(logout -> {
  logout.logoutUrl("/logout") // 退出登录路径
     .logoutSuccessUrl("/login.html") // 退出登录后跳转的路径
     .clearAuthentication(true) //清除认证状态,默认为true
     .invalidateHttpSession(true); // 销毁HttpSession对象,默认为true
});

二、授权功能实现

修改认证逻辑


// 自定义认证逻辑
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  // 1.构造查询条件
  QueryWrapper<Users> wrapper = new QueryWrapper<Users>().eq("username", username);
  // 2.查询用户
  Users users = userMapper.selectOne(wrapper);
  if (users == null){
    return null;
   }
  // 3.查询用户权限
  List<Permission> permissions = userMapper.findPermissions(username);
  // 4.将权限集合转为Security的权限类型集合
  List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
  for (Permission permission : permissions) {
    grantedAuthorities.add(new SimpleGrantedAuthority(permission.getUrl()));
   }
  // 5.封装为UserDetails对象
  UserDetails userDetails = User.withUsername(users.getUsername())
     .password(users.getPassword())
     .authorities(grantedAuthorities)
     .build();
  // 6.返回封装好的UserDetails对象
  return userDetails;
}

使用在资源上添加访问权限

@PreAuthorize("hasAnyAuthority('user')")
@GetMapping("/user")
public String test() {
  return "";
}

你可能感兴趣的:(Spring Security 入门)