[Springsecurity]springsecurity 基础实战

本文介绍了Spring Security的配置,包括密码加密方式(BCryptPasswordEncoder和SCryptPasswordEncoder)和身份验证设置,以及如何扩展WebSecurityConfigurerAdapter。此外,还讲解了UserDetails接口和CustomUserDetails类在用户权限描述中的作用,以及UserDetailsService的loadUserByUsername方法用于获取用户信息。最后,展示了authenticate方法如何校验账号密码,并根据不同的加密算法进行匹配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.springsecurity配置

对于springsecurity的配置要扩展继承WebSecurityConfigurerAdapter方法,主要有两个任务,一个是配置密码的加密方式,第二个是配置身份验证。通过配置身份验证,spring security身份验证可以通过过滤拦截,验证客户端的身份的有效性,并返回赋予其权限。

/**
 * WebSecurityConfigurerAdapter中有相当多的springsecurity默认配置,如果需要自定义其中的配置需要进行对它扩展
 */
@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthenticationProviderService authenticationProvider;
    /**
     * PasswordEncoder 承担两个任务:
     * 1.  将密码进行编码
     * 2。 验证密码是否与现有编码相匹配
     *
     * NoOpPasswordEncoder 实例会将密码视为普通文本。不会对密码进行加密或者哈希操作。为了进行匹配,NoOpPasswordEncoder
     * 只会使用String类底层的equals(Object o)方法来比较字符串。所以,不应该在生产环境中使用该方式。
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SCryptPasswordEncoder sCryptPasswordEncoder() {
        return new SCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(authenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //todo:
        /**
         * 身份验证方式,表单验证方式。
         *
         * 在客户端访问时,要附加前缀Basic,后面还要加上包含用户名和密码的字符串Base64编码,用冒号(:)分隔。
         */
        http.formLogin()
                //登录成功后跳转到主页面
                .defaultSuccessUrl("/main", true);
        http.authorizeRequests().anyRequest().authenticated();
    }
}

2.UserDetails用户的详细描述

在springsecurity中该类继承 UserDetails用于描述用户的密码权限等内容,在过身份验证滤拦截器中拿到该类的信息进行对账号密码的比对,同时提取权限信息用于赋予登录客户端的权限信息。

@Data
public class CustomUserDetails implements UserDetails {

    //note: 为了说明没有User实体CustomUserDetails就毫无意义,所以将该字段用final修饰
    private final User user;

    public CustomUserDetails(User user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return user.getAuthorities().stream()
                //将在数据库中找到的该用户的每个权限名称映射到一个SimpleGrantedAuthority
                   .map(a -> new SimpleGrantedAuthority(a.getName()))
                //以列表形式收集并返回 SimpleGrantedAuthority的所有实例
                   .collect(Collectors.toList());
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

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

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

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

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

    public final User getUser() {
        return user;
    }
}

3.loadUserByUsername加载用户信息

loadUserByUsername是UserDetailsService抽象类中的一个方法,通过继承实现该方法达到获取用户信息描述(UserDetails)的能力,随后进行对账号密码的比对。

    @Override
    public CustomUserDetails loadUserByUsername(String username) {
        //创建一个异常类
        Supplier<UsernameNotFoundException> s =
                () -> new UsernameNotFoundException("Problem during authentication!");

        //如果返回为空,则返回异常类。如果查询的到则返回User对象。
        User u = userRepository.findUserByUsername(username).orElseThrow(s);

        return new CustomUserDetails(u);
    }

4.authenticate 截取陌生客户端的鉴权信息(账号密码)

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();


        /**
         * 使用UserDetailsService (JpaUserDetailsService实现UserDetailsService)
         * 的loadUserByUsername方法查询用户详细信息
         */

        CustomUserDetails user = userDetailsService.loadUserByUsername(username);

        switch (user.getUser().getAlgorithm()) {
            case BCRYPT:
                return checkPassword(user, password, bCryptPasswordEncoder);
            case SCRYPT:
                return checkPassword(user, password, sCryptPasswordEncoder);
        }

        throw new  BadCredentialsException("Bad credentials");
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(aClass);
    }

5.checkPassword 比对账号密码并返回权限

    /**
     * 核对密码
     * @param user 查询的人员信息
     * @param rawPassword 密码
     * @param encoder 编码方式
     * @return
     */
    private Authentication checkPassword(CustomUserDetails user, String rawPassword, PasswordEncoder encoder) {
        if (encoder.matches(rawPassword, user.getPassword())) {
            return new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), user.getAuthorities());
        } else {
            throw new BadCredentialsException("Bad credentials");
        }
    }

6. 完整代码

6.1 代码目录

 6.2 代码下载

整理好后会附上链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wL魔法师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值