SpringSecurity权限管理框架系列(二)-Spring Security框架实现用户登录认证流程详解

1、Spring Security登录认证的核心

Spring Security框架完成用户登录认证的核心就在与org.springframework.security.core.userdetails包下的UserDetailsService接口

2、UserDetailsService接口详解

2.1 UserDetailsService接口的定义

UserDetailsService接口只有一个抽象方法就是loadUserByUsername(String username),
SpringSecurity权限管理框架系列(二)-Spring Security框架实现用户登录认证流程详解_第1张图片
UserDetailsService接口的返回值是UserDetails接口, 这又是一个接口,Spring Security框架提供了它的实现类org.springframework.security.core.userdetails包下的User类对象
SpringSecurity权限管理框架系列(二)-Spring Security框架实现用户登录认证流程详解_第2张图片

2.2 UserDetailsService接口的已有实现类有哪些

我们再看看UserDetailsService接口提供的默认的实现类

  • JdbcDaoImpl(还有一个子类JdbcUserDetailsManager)
  • InMemoryUserDetailsManager
    SpringSecurity权限管理框架系列(二)-Spring Security框架实现用户登录认证流程详解_第3张图片
    SpringSecurity权限管理框架系列(二)-Spring Security框架实现用户登录认证流程详解_第4张图片
2.2.1 UserDetailsService接口的实现类JdbcDaoImpl的实现

我们在分别看看UserDetailsService的实现类JdbcDaoImplInMemoryUserDetailsManager都是怎么实现这个loadUserByUsername(String username)方法的
SpringSecurity权限管理框架系列(二)-Spring Security框架实现用户登录认证流程详解_第5张图片
我们在分别看看UserDetailsService的实现类JdbcDaoImpl中loadUserByUsername(String username)方法的实现过程
SpringSecurity权限管理框架系列(二)-Spring Security框架实现用户登录认证流程详解_第6张图片
下图主要就是验证一下createuserDetails方法的返回值是不是一个User对象,没有什么具体含义。
SpringSecurity权限管理框架系列(二)-Spring Security框架实现用户登录认证流程详解_第7张图片

2.2.2 UserDetailsService接口的实现类InMemoryUserDetailsManager的实现

看看InMemoryUserDetailsManager类的loadUserByUsername(String username)方法的具体实现
SpringSecurity权限管理框架系列(二)-Spring Security框架实现用户登录认证流程详解_第8张图片
通过以上的源码解读, 我们可以大胆猜测一下一个是不是只要我们在我们自己的项目中定义一个自定义的实现类实现这个UserDetailsService,就可以完成我们自定义的登录认证逻辑, 接下来我们自己写一个实现类来尝试一下。

3、自定义实现类实现UserDetailsService接口

自定义实现类MyUserDetailsServiceImpl

package com.yige.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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.Service;

@Service
public class MyUserDetailsServiceImpl implements UserDetailsService {

    private static final String USERNAME = "test";
    private static final String PASSWORD = "test";

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        if(!USERNAME.equals(userName)){
            throw new UsernameNotFoundException("用户名不存在");
        }

        UserDetails userDetails = new User(USERNAME,
                PASSWORD,
                AuthorityUtils.commaSeparatedStringToAuthorityList("admin,common"));
       return userDetails;
    }
}

重启项目看看,能不能生效。

登录输入用户名"test", 密码"test",发现页面刷新了一下又回到登录页面了, 怎么回事?
SpringSecurity权限管理框架系列(二)-Spring Security框架实现用户登录认证流程详解_第9张图片
检查了一下后台程序,发现后台报错了,没有使用任何的PasswordEncoder,我们输入的密码没有用加密工具进行加密

SpringSecurity权限管理框架系列(二)-Spring Security框架实现用户登录认证流程详解_第10张图片
Spring Security其实已经给我们提供了很多的PasswordEncoder, 我们来看一下
org.springframework.security.crypto.password包下有一个PasswordEncoder接口,看看他的实现类
SpringSecurity权限管理框架系列(二)-Spring Security框架实现用户登录认证流程详解_第11张图片
所有这里就很简单了啊,两种方式可以搞定:

  • 我们只需要在我们的项目里面整一个Spring Security的配置类,把这个PasswordEncoder的任意一个我们需要用来加密密码的实现类的Bean注入到容器里面,就可以直接拿来使用
  • 我们也可以自己自定义密码加密的实现类实现PasswordEncoder接口, 注入Bean到容器

这里我们采用第一种方式,人家已经实现了, 不需要重复造轮子,当然如果你确信你自己造的轮子更好就可以用你自己的。

package com.yige.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public BCryptPasswordEncoder getPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

修改MyUserDetailsServiceImpl类如下:

package com.yige.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

/**
 * @Author: karma
 * @Date: 2022/3/21 0021 - 03 - 21 - 11:20
 * @Description: com.yige.service.impl
 * @version: 1.0
 */
@Service
public class MyUserDetailsServiceImpl implements UserDetailsService {

    private static final String USERNAME = "test";
    private static final String PASSWORD = "test";

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        if(!USERNAME.equals(userName)){
            throw new UsernameNotFoundException("用户名不存在");
        }

        return new User(userName, passwordEncoder.encode(PASSWORD), AuthorityUtils.commaSeparatedStringToAuthorityList("admin,common"));
    }
}

重启项目再次测试。

登录输入用户名"test", 密码"test",确认登录,

SpringSecurity权限管理框架系列(二)-Spring Security框架实现用户登录认证流程详解_第12张图片
页面跳转到http://localhost:8080,说明我们的登录认证成功了。以上测试说明我们自定义的登录认证逻辑生效了,

4、测试结论

由以上测试我们可以得出结论,当我们自定义实现了UserDetailsService逻辑并注入bean到容器之后, 项目再次启动之后,在进行登录认证的时候就会使用我们自定义的逻辑来进行处理, 不会在使用Spring Security框架默认的逻辑进行认证处理。

5、下一节内容

当前我们的实现都是把用户名、密码、权限都是硬编码在我们自己的代码中, 这样肯定是不行的, 下一节我们着重说说怎么让我们自定义的登录认证跟我们的数据库搭配使用,从数据库中获取用户的数据信息来完成登录认证的过程。

你可能感兴趣的:(Spring,Security,Spring全家桶,spring,security,权限框架,spring,springboot)