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

springSecurity入门(5.7版本之前)

准备

依赖

<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>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.13</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>
    </dependencies>

用户实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String username;
    private String password;
}

controller接口

@RestController
@RequestMapping("/user")
public class controller {

    @Autowired
    private LoginService loginService;
    
    @PostMapping("/login")
    public String login(@RequestBody User userLogin){
        String token = loginService.login(userLogin);
        return token;
    }
    @PostMapping("/hello")
    @PreAuthorize("hasAuthority('user')")//这里添加访问该接口需要的角色为 ‘user’
    public String hello(){
        return "Hello";
    }
}

集成

首先实现两个接口

  • UserDetailsService–springsecurity默认有认证的账号密码,我们不使用它自带的,实现这个接口重写它的方法用于获取我们真正的用户数据
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if (username == null){//用户不存在的处理
            throw new UsernameNotFoundException("用户不存在");
        }
        //这里可以访问数据库获取用户信息
        User user=new User();
        user.setUsername("admin");
        //这里的密码是admin使用BCryptPasswordEncoder加密后的结果,数据库中存储的一定是加密后的密码
        user.setPassword("$2a$10$iLLMvl6E9t7qDYexmu65VOM.tVgGwp5wHh5hSG4aC7rYuRoA6I//m");

        //用户权限列表,假设用户有一个user角色
        List<String> list =new ArrayList<String>(Arrays.asList("user"));
        
        //封装一个loginUser对象,也是springsecurity内置对象的实现类
        LoginUser loginUser = new LoginUser(user,list);
        return loginUser;
    }
}

  • UserDetails–spring认证需要的对象,实现这个方法加入我们自己的逻辑
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginUser implements UserDetails {
    private User user;//我们自己的用户对象

    private List<String> permissions;//权限列表

    @JSONField(serialize = false)//一定要加,否则集成redis运行会出问题
    private List<SimpleGrantedAuthority> authorities;//提取出来的字段,提高效率

    //构造器--自定义
    public LoginUser(User user, List<String> permissions) {
        this.user = user;
        this.permissions = permissions;
    }
        @Override
        //权限集合获取的方法,这个方法的逻辑也可以改写,如果User中有权限集合可以从User中获取
        public Collection<? extends GrantedAuthority> getAuthorities () {
            if (authorities == null)//如果已经有这个集合,我们就不用再赋值一次,直接使用当前的authorities
             authorities = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());

            return authorities;
        }

        @Override
        //获取用户密码的方法
        public String getPassword () {
            return user.getPassword();
        }

        @Override
        //获取用户名
        public String getUsername () {
            return user.getUsername();
        }

        //以下四个方法返回值都为true即可
        @Override
        public boolean isAccountNonExpired () {
            return true;
        }

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

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

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

进行配置

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法级别的安全控制
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private JwtFilter jwtFilter; // 过滤器链对象,用于认证和鉴权

    @Bean
    public PasswordEncoder passwordEncoder() {
        // 使用 BCrypt 加密算法进行密码加密与比对
        return new BCryptPasswordEncoder();
    }

    // 注入 AuthenticationManager 对象,用于调用认证方法
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean(); // 返回 AuthenticationManager 实例
    }

    // 配置请求的权限控制
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // 关闭 CSRF 保护
                .csrf().disable()
                // 不通过 Session 获取 SecurityContext,设置为无状态
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                // 配置请求的授权规则
                .authorizeRequests()
                // 对于登录接口允许匿名访问
                .antMatchers("/user/login").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();
        
        // 在 UsernamePasswordAuthenticationFilter 前添加 JWT 过滤器
        http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
    }

}

过滤器

@Component
public class JwtFilter extends OncePerRequestFilter {//实现这个接口用于每个请求只会被处理一次

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 从请求头中获取 token
        String token = request.getHeader("token");
        
        // 如果没有 token,继续执行后续的过滤器(例如登录流程)
        if (StrUtil.isBlank(token)) {
            filterChain.doFilter(request, response);
            return;
        }

        // 从 token 中提取用户信息
        // 这里可以通过 JWT 工具类获取 token 中的用户信息,或从 Redis 中获取用户信息
        // 为演示目的,直接实例化一个用户对象
        User user = new User();
        user.setUsername("admin"); // 设置用户名
        user.setPassword("$2a$10$iLLMvl6E9t7qDYexmu65VOM.tVgGwp5wHh5hSG4aC7rYuRoA6I//m"); // 设置密码(已加密)
        
        // 创建一个权限列表,并添加权限
        List<String> list = new ArrayList<>();
        list.add("user");
        
        
        // 创建 LoginUser 对象,包含用户信息和权限
        LoginUser loginUser = new LoginUser(user, list);

        // 登录成功后,使用 UsernamePasswordAuthenticationToken 存储用户信息和权限信息
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());

        // 将用户信息存入 SecurityContext,后续可以从这里取出用户信息
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        // 继续执行后续的过滤器链
        filterChain.doFilter(request, response);
    }

}

在LoginService实现类中进行认证

@Service
public class LoginService {
    @Resource
    private AuthenticationManager manager; // 注入 AuthenticationManager,用于用户认证

    public String login(User user) {
        // 创建 UsernamePasswordAuthenticationToken,用于认证
        UsernamePasswordAuthenticationToken userAuthentication = 
                new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
        
        // 调用 AuthenticationManager 的方法进行用户认证
        Authentication authenticate = manager.authenticate(userAuthentication);
        
        // 如果认证成功(authenticate 不为空),进入生成 token 的逻辑
        if (!Objects.isNull(authenticate)) {
            // 获取 LoginUser 对象
            LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
            // 获取真正的 User 对象
            User u = loginUser.getUser();

            // 生成 token
            String token = JWT
                    .create()
                    .setPayload("userLoginId", u.getId()) // 设置用户 ID 为负载
                    .setIssuedAt(new Date()) // 设置 token 的发放时间
                    .setExpiresAt(new Date(System.currentTimeMillis() + DateUnit.WEEK.getMillis())) // 设置过期时间为一周
                    .setSigner("HMD5", "salt".getBytes(StandardCharsets.UTF_8)) // 设置加密算法和盐值
                    .sign(); // 生成并签名 token
            
            // 返回生成的 token
            return token;
        }
        // 如果认证失败,抛出异常
        throw new RuntimeException("用户名或密码错误");
    }
}

测试

在这里插入图片描述

携带刚刚生成的token去访问需要权限的接口

在这里插入图片描述


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

相关文章:

  • 浅谈鸿蒙生态崛起的机遇
  • 常见协议端口号
  • 「Mac畅玩鸿蒙与硬件12」鸿蒙UI组件篇2 - Image组件的使用
  • 科研项目:利用AI大模型获得基金资助的10个原则
  • RNN在训练中存在的问题
  • 用Pyhon写一款简单的益智类小游戏——2048
  • 各种语言的列表推导式与三元?表达式,C++,python,rust,swift,go
  • ubuntu20.04 加固方案-设置重复登录失败后锁定时间限制
  • flutter_vscode常用快捷键
  • Spring Boot租房管理系统:功能实现与优化
  • 美团嵌入式面试题及参考答案(无人机团队)
  • 云-转录组平台升级解锁更多实用交互式功能
  • 【React 的理解】
  • java小白到架构师技术图谱
  • 流媒体转发服务器的应用场景与原理
  • Linux——五种IO模型
  • linux命令之top(Linux Command Top)
  • day14:RSYNC同步
  • 第72期 | GPTSecurity周报
  • 书生-第四期闯关:完成SSH连接与端口映射并运行hello_world.py
  • 如何使用 Vue CLI 创建 Vue 项目?
  • Java迭代器:深入理解与应用
  • 二百七十四、Kettle——ClickHouse中对错误数据表中进行数据修复(实时)
  • Spark集群管理脚本详解
  • 【数据结构-邻项消除】力扣2211. 统计道路上的碰撞次数
  • UDP-鼠李糖合成酶基因的克隆与鉴定-文献精读76