当前位置: 首页 > article >正文

Spring Security使用指南

        文章讲解了Spring Security的各个模块和功能。其中包括用户认证、角色授权、密码加密和解密等。文章还提供了详细的代码示例,帮助读者快速上手并理解Spring Security的使用。总之,本文是一篇全面而实用的Spring Security使用指南,对于想要学习和使用Spring Security的开发者来说是一份非常有价值的资料。

一、入门使用

1,引入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

2,访问请求

假设有如下的接口,当访问时会自动下载权限框架的登录页请求

3,自定义登录页

准备自定义的静态资源

添加配置

@Configuration
public class SecurityConfig {

  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

    // 配置表单登录
    http.formLogin()
        // 指定自定义的登录页面 URL
        .loginPage("/login.html")
        // 指定登录请求的处理 URL
        .loginProcessingUrl("/login")
        // 允许所有用户访问登录页面和登录处理 URL
        .permitAll()
        .and()
        // 配置授权请求
        .authorizeRequests()
        // 允许所有用户访问以下路径
        .antMatchers( "/css/**", "/js/**", "/images/**")
        .permitAll()
        // 对于其他所有请求,要求用户必须经过身份验证
        .anyRequest()
        .authenticated()
        .and()
        // 禁用 CSRF 保护
        .csrf()
        .disable();

    // 构建并返回安全过滤链
    return http.build();
  }

}

呈现效果

二、自定义认证

1,基于内存的认证

    @Bean
    PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Bean
    public UserDetailsService users() {
        UserDetails user = User.builder()
                .username("user")
                .password("123456")
                .roles("USER")
                .build();
        UserDetails admin = User.builder()
                .username("admin")
                .password("112233")
                .roles("USER", "ADMIN")
                .build();
        return new InMemoryUserDetailsManager(user, admin);
    }

2,BCrypt密码加密

BCrypt就是一款加密工具,可以比较方便地实现数据的加密工作。也可以简单理解为它内部自己实现了随机加盐处理。例如,使用MD5加密,每次加密后的密文其实都是一样的,这样就方便了MD5通过大数据的方式进行破解。BCrypt生成的密文长度是60,而MD5的长度是32。

import org.springframework.security.crypto.bcrypt.BCrypt;

public class Main {
    public static void main(String[] args) {
        byte[] bytes = "123456".getBytes();
        System.out.println(bytes);
        String hashpw = BCrypt.hashpw(bytes, BCrypt.gensalt());
        System.out.println("password-one:" + hashpw);
        String hash = BCrypt.hashpw(bytes, BCrypt.gensalt());
        System.out.println("password-two:" + hash);
        System.out.println("------------------------");
        System.out.println(BCrypt.checkpw(bytes, hash));
        System.out.println(BCrypt.checkpw(bytes, hashpw));
        System.out.println(BCrypt.checkpw("123456", hashpw));
    }
}

三、基于JDBC数据库实现认证

1,执行流程

2,基于内存代码执行

在Spring Security框架中提供了一个UserDetailsService 接口,它的主要作用是提供用户详细信息。具体来说,当用户尝试进行身份验证时,UserDetailsService 会被调用,以获取与用户相关的详细信息。这些详细信息包括用户的用户名、密码、角色等

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.stereotype.Component;

@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        if (username.equals("user")) {
            return User.builder()
                    .username(username)
                    .password("$2a$10$mPMldGTPYijVDItlA3/SsuAlv8OK2Rfz7VNbgdU4peyDCsxRqS95y")
                    .roles("USER")
                    .build();
        }

        if (username.equals("admin")) {
            return User.builder()
                    .username(username)
                    .password("$2a$10$mPMldGTPYijVDItlA3/SsuAlv8OK2Rfz7VNbgdU4peyDCsxRqS95y")
                    .roles("USER", "ADMIN")
                    .build();
        }

        return null;
    }
}

3,基于数据库代码执行

@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // 根据用户名查询用户
        User user = userMapper.findByUsername(username);
        Assert.isTrue(user != null, "用户不存在");

        // 建立角色
        SimpleGrantedAuthority userRole = new SimpleGrantedAuthority("user");
        SimpleGrantedAuthority adminRole = new SimpleGrantedAuthority("admin");
        List<SimpleGrantedAuthority> roleList = new ArrayList<>();
        roleList.add(userRole);
        roleList.add(adminRole);

        return new org.springframework.security.core.userdetails.User(user.getUsername(),
                user.getPassword(), roleList);
    }
    
}

4,自定义框架的USER类

import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

@Data
public class UserAuth implements UserDetails {

    private String username; //固定不可更改
    private String password;//固定不可更改
    private String nickName;  //扩展属性  昵称
    private List<String> roles; //角色列表


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        if (roles == null) return null;
        return roles.stream()
                .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                .collect(Collectors.toList());
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // 根据用户名查询用户
        User user = userMapper.findByUsername(username);
        Assert.isTrue(user != null, "用户不存在");

        UserAuth userAuth = new UserAuth();
        BeanUtils.copyProperties(user, userAuth);

        // 建立角色
        List<String> roles=new ArrayList<>();
        if ("user@qq.com".equals(user.getUsername())) {
            roles.add("USER");
        }

        if ("admin@qq.com".equals(user.getUsername())) {
            roles.add("USER");
            roles.add("ADMIN");
        }
        userAuth.setRoles(roles);

        return userAuth;
    }

}

// 获取认证对象主体
UserAuth userAuth = (UserAuth) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

四、授权

1,简单WEB授权

新增两个接口

分配角色

当使用user角色访问/admin时,便会返回403权限不足

2,控制操作方法

● permitAll() 方法,所有用户可访问。
● denyAll() 方法,所有用户不可访问。
● authenticated() 方法,登录用户可访问。
● anonymous() 方法,匿名用户可访问。
● rememberMe() 方法,通过 remember me 登录的用户可访问。
● fullyAuthenticated() 方法,非 remember me 登录的用户可访问。
● hasIpAddress(String ipaddressExpression) 方法,来自指定 IP 表达式的用户可访问。
● hasRole(String role) 方法, 拥有指定角色的用户可访问,传入的角色将被自动增加 “ROLE_”   前缀。 
● hasAnyRole(String... roles) 方法,拥有指定任意角色的用户可访问。传入的角色将被自动增加 “ROLE_” 前缀。
● hasAuthority(String authority) 方法,拥有指定权限( authority )的用户可访问。
● hasAnyAuthority(String... authorities) 方法,拥有指定任意权限( authority )的用户可访问。

5、整合JWT

1,实现流程

2,注册认证管理

@Configuration
public class SecurityConfig {

  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeRequests().antMatchers("/security/login").permitAll();
    http.csrf().disable();
    return http.build();
  }


  @Bean
  public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
    return configuration.getAuthenticationManager();
  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }


  @Bean
  public UserDetailsService users() {
    UserDetails user = User.builder()
        .username("user")
        .password("$2a$10$2VCyByZ5oeiXCEN73wvBB.xpmJgPBbZVS/Aallmdyij2G7hmAKQTG")
        .roles("USER")
        .build();
    UserDetails admin = User.builder()
        .username("admin")
        .password("$2a$10$cRH8iMMh6XO0T.ssZ/8qVOo8ThWs/qfntIH3a7yfpbPd05h9ZGx8y")
        .roles("USER", "ADMIN")
        .build();
    return new InMemoryUserDetailsManager(user, admin);
  }

}

3,登录接口

@PostMapping("/login")
    public String login(@RequestBody LoginDto loginDto){
        // 使用接收到的用户名和密码创建一个认证令牌对象。
        UsernamePasswordAuthenticationToken authenticationToken =
            new UsernamePasswordAuthenticationToken(loginDto.getUsername(), loginDto.getPassword());
        // 调用Spring Security的认证管理器来验证提供的认证令牌。
        // 如果用户名或密码不正确,这一步会抛出异常。
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        if (authenticate.isAuthenticated()) {
            // 获取认证成功的主体
            Object principal = authenticate.getPrincipal();
            Map<String, Object> claims = new HashMap<>();
            claims.put("user", principal);
            String token = JWTUtil.createToken(claims, JWT_KEY.getBytes());
            log.info("token: {}", token);
            return token;
        }
        return "";
    }

4,自定义认证管理

@Component
public class TokenAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
  @Override
  public AuthorizationDecision check(Supplier<Authentication> authentication,
                                     RequestAuthorizationContext requestAuthorizationContext) {
    HttpServletRequest request = requestAuthorizationContext.getRequest();
    String token = request.getHeader("token");
    if (!StringUtils.hasText(token)) {
      return new AuthorizationDecision(false);
    }

    Claims claims = JwtUtil.parseJWT(JWT_KEY, token);
    //获取userAuth
    UserAuth user = JSONObject.parseObject(JSON.toJSONString(claims.get("user")),UserAuth.class);

//存入上下文
        UsernamePasswordAuthenticationToken auth
                =new UsernamePasswordAuthenticationToken( userAuth, userAuth.getPassword(), userAuth.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(auth);

    if (user != null) {
      return new AuthorizationDecision(false);
    }

    String requestURI = request.getRequestURI();
    if (user.getRoles().contains("admin")) {
      if("/hello/admin".equals(requestURI)){
        return new AuthorizationDecision(true);
      }
    }

    if(user.getRoles().contains("USER")){
      if("/hello/user".equals(requestURI)){
        return new AuthorizationDecision(true);
      }
    }

    return new AuthorizationDecision(false);
  }
}
  @Autowired
  private TokenAuthorizationManager tokenAuthorizationManager;

  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests().antMatchers("/security/login").permitAll()
        .anyRequest().access(tokenAuthorizationManager);
    http.csrf().disable();
        //关闭session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //关闭缓存
        http.headers().cacheControl().disable();
    return http.build();
  }


http://www.kler.cn/a/473130.html

相关文章:

  • Flink源码解析之:Flink on k8s 客户端提交任务源码分析
  • n 维数组(张量)关于轴 axis 的理解
  • vulnhub靶场【DC系列】之7
  • HTMLElement、customElements及元素拓展
  • 海陵HLK-TX510人脸识别模块 stm32使用
  • SQL从入门到实战
  • 代码随想录 链表 test 6
  • 东京大学联合Adobe提出基于指令的图像编辑模型InstructMove,可通过观察视频中的动作来实现基于指令的图像编辑。
  • Selenium 进行网页自动化操作的一个示例,绕过一些网站的自动化检测。python编程
  • 『 Linux 』高级IO (三) - Epoll模型的封装与EpollEchoServer服务器
  • C#里对已经存在的文件进行压缩生成ZIP文件
  • FPGA车牌识别
  • 基于需求文档、设计文档、测试用例的测试答疑助手
  • 用Portainer实现对Docker容器的管理(四)
  • 深度学习与计算机视觉 (博士)
  • 【JAVA基础】Collections方法的具体使用方法
  • 计算机网络 笔记 物理层
  • 【递归与分治】Leetcode23:合并K个升序链表
  • Redis--20--大Key问题解析
  • Mono里运行C#脚本26—CEE_ADD/MONO_CEE_ADD/OP_IADD/X86_ADD的转换过程
  • java项目学科竞赛管理系统的设计与实现源码(springboot+mysql+vue)
  • 预训练语言模型——BERT
  • 【免费】2000-2019年各省技术市场成交额数据
  • 字玩FontPlayer开发笔记9 Tauri2打包应用
  • Golang的并发编程框架比较
  • ASP.NET Core实现微服务--什么是微服务