实现登录认证功能

2022-09-05 SpringSecurity

SpringBoot项目引入SpringSecurity安全框架后,访问接口会自动跳转到登录页面,这个页面的账号是默认固定为user,密码会在控制台打印。

实际开发中我们肯定会到数据库中进行账号密码比对,因此我们需要实现SpringSecurity给我们提供的方法。但是这个User对象是我们自己定义的,SpringSecurity是肯定不认识的,因此我们需要对User对象进行简单的处理。

# 处理User对象

User对象一般是和数据库相关联的,为了减少集成SpringSecurity对我们代码的影响,我们新建一个LoginUser类。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser implements UserDetails {


    private User user;


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @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;
    }
}

代码分析:

  • 实现UserDetails相当于让SpringSecurity认识了这个User对象,getAuthorities是保存权限码的,这个我们到鉴权相关的篇章再说。

  • getUsernamegetPassword分别代表账号和密码

  • 剩下的四个都为true才代表当前用户可用,只要有一个为false都代表登录失败,可以根据自己的项目映射相关字段。

# 让SpringSecurity实现从数据库查询对象

我用ArrayList模拟用户数据,findUserByUsername方法相当于从数据库查询对象,loadUserByUsername是实现SpringSecurity接口提供的方法,实现自定义用户信息,而不是框架默认的user用户。

@Service
public class UserDetailServiceImpl implements UserDetailsService {

    public static List<User> userList = new ArrayList<User>() {{
        add(new User("1", "user1", "123456", "用户一"));
        add(new User("2", "user2", "123456", "用户二"));
        add(new User("3", "user3", "123456", "用户三"));
        add(new User("4", "user4", "123456", "用户四"));
        add(new User("5", "user5", "123456", "用户五"));
    }};

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 查询用户信息
        User user = findUserByUsername(username);
        // 如果找不到用户则抛出异常
        if (user == null) {
            throw new RuntimeException("用户名或密码错误");
        }
        //TODO 根据用户查询权限信息 添加到LoginUser中
        return new LoginUser(user);
    }

    /**
     * 根据用户名查询用户
     * @param username 用户名
     */
    public static User findUserByUsername(String username) {
        for (User user : userList) {
            if (user.getUsername().equals(username)) {
                return user;
            }
        }
        return null;
    }
}

SpringSecurity默认会对密码进行加解密进行比对,所以在登录页面输入密码为123456发现提示用户或密码错误。

# 配置密码加密存储

实际项目中我们不会把密码明文存储在数据库中,默认使用的PasswordEncoder要求数据库中的密码格式为:{id}password 。它会根据id去判断密码的加密方式。但是我们一般不会采用这种方式。所以就需要替换PasswordEncoder。

我们一般使用SpringSecurity为我们提供的BCryptPasswordEncoder,只需要使用把BCryptPasswordEncoder对象注入Spring容器中,SpringSecurity就会使用该PasswordEncoder来进行密码校验。

我们可以定义一个SpringSecurity的配置类,SpringSecurity要求这个配置类要继承WebSecurityConfigurerAdapter。

@Configuration
public class SecurityConfig {

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

加密解密测试类,encode获取密码加密,matches进行密码比对。

@Slf4j
@SpringBootTest
class JenkinsDemoApplicationTests {
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Test
    void contextLoads() {
        log.info("密码加密:{}", passwordEncoder.encode("123456"));
        log.info("比对结果:{}", passwordEncoder.matches("123456", "$2a$10$7B9Ie1iy.50PecAACzE………………"));
    }
}

我们修改模拟的"数据库",然后重启一下项目,访问页面输入账号密码,发现已经能够正常访问了。

public static List<User> userList = new ArrayList<User>() {{
    add(new User("1", "user1", "$2a$10$7B9Ie1iy.50PecAACzEAEOJ3m5WlkAJ7S0MHjOiLiTuKRvTdMYYYW", "用户一"));
    add(new User("2", "user2", "$2a$10$7B9Ie1iy.50PecAACzEAEOJ3m5WlkAJ7S0MHjOiLiTuKRvTdMYYYW", "用户二"));
    add(new User("3", "user3", "$2a$10$7B9Ie1iy.50PecAACzEAEOJ3m5WlkAJ7S0MHjOiLiTuKRvTdMYYYW", "用户三"));
    add(new User("4", "user4", "$2a$10$7B9Ie1iy.50PecAACzEAEOJ3m5WlkAJ7S0MHjOiLiTuKRvTdMYYYW", "用户四"));
    add(new User("5", "user5", "$2a$10$7B9Ie1iy.50PecAACzEAEOJ3m5WlkAJ7S0MHjOiLiTuKRvTdMYYYW", "用户五"));
}};
上次更新: 1 年前