spring-security核心类解析--整理中....

前言

这一篇文章可能会陆续更新很久,主要是不一定有空,有精力整理那么多。不太习惯连载教程,网上其他的博客也很多了。可不太令我满意的是,大多数spring-security的配置都是停留在xml配置,springboot如何整合spring-security讲解的不多。所以本篇博客的定位是基于javaConfig,结合springboot的一些自动配置,详解一些spring-security的核心类和接口(建议有一定spring-security配置经验的人观看,不然你会不知道我在扯什么)。

spring-security最核心的接口是AuthenticationManager

public interface AuthenticationManager {

    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;
}

记住,全局唯一身份管理器,AuthenticationManager是一个用来处理认证(Authentication)请求的接口。在其中只定义了一个方法authenticate(),该方法只接收一个代表认证请求的Authentication对象作为参数,如果认证成功,则会返回一个封装了当前用户权限等信息的Authentication对象进行返回。

spring-security为了方便用户的配置,提供了很多接口,很多配置适配器类,来适应不同用户的需求,所以可能把一些小白搞得晕头转向,明明就是想从数据库加载一个简单的用户,却不得不搞清楚那么多类。但是请记住,最核心的身份认证就是这个接口。
他有两个核心的子类

AuthenticationManager
----AuthenticationManagerDelegator
----ProviderManager

ProviderManager是我们打交道比较多的一个类,而AuthenticationManagerDelegator看名字就可以发现这是一个委托类,通常是由SecurityBuilder接口的子类来帮助你配置生成一个身份管理器。spring的命名很规范,后缀带有Builder字样的类,都可以理解为“帮助你配置一个类”的工具类。
说回重要的ProviderManager

public class ProviderManager implements AuthenticationManager, MessageSourceAware,
        InitializingBean {

    private List providers = Collections.emptyList();
    private AuthenticationManager parent;

    public ProviderManager(List providers) {
        this(providers, null);
    }

    public ProviderManager(List providers,
            AuthenticationManager parent) {
        Assert.notNull(providers, "providers list cannot be null");
        this.providers = providers;
        this.parent = parent;
        checkState();
    }
}

截取它的部分重要代码,发现内部是维护了一个List,对,非常之绕!AuthenticationManager自己是一个接口不干活,靠他的实现类ProviderManager去认证请求,他自己又不干活,交给内部的一个AuthenticationProvider列表去认证请求。绕是绕,但是有它的道理,学过shiro的同学可能知道realm这个东西,就跟spring-security中的AuthenticationProvider类似,设计成一个列表,意思是一个用户可能用多种方式进行认证(邮箱登录,手机号码登录,指纹登录?,第三方登录等等乱编的登录)。只需要通过一个即可认证成功,不过暂时没有找到类似shiro的认证策略的配置。
身份提供者AuthenticationProvider

public interface AuthenticationProvider {

    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;

    boolean supports(Class authentication);
}

AuthenticationMannager接口很像,多了一个supports方法,表示这个身份提供者支持认证什么类型的身份信息,只有返回true,才会执行authenticate方法。

AuthenticationProvider
----AbstractUserDetailsAuthenticationProvider
    ----DaoAuthenticationProvider
----RememberMeAuthenticationProvider
----xxxxProvider

有很多默认的实现,不过最常用的当然就是第一个类了:DaoAuthenticationProvider,从某个数据源加载身份信息。

public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

    private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
    //密码相关
    private PasswordEncoder passwordEncoder;

    private String userNotFoundEncodedPassword;
    //盐相关
    private SaltSource saltSource;
    //用户相关
    private UserDetailsService userDetailsService;
}

基本的认证相关的类到这儿就结束了,主要就是看DaoAuthenticationProvider内部维护的UserDetailsService接口是个什么东西了。看名字就可以猜出来,这是个跟用户打交道的接口。

下面介绍UserDetailsService接口

public interface UserDetailsService {

    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

以及它的一个扩展接口UserDetailsManager

public interface UserDetailsManager extends UserDetailsService {

    void createUser(UserDetails user);

    void updateUser(UserDetails user);

    void deleteUser(String username);

    void changePassword(String oldPassword, String newPassword);

    boolean userExists(String username);

}

于是加载用户就分成了两条路。
spring-security核心类解析--整理中...._第1张图片
当然,这一切的前提都是你选择使用spring-security的默认实现。其实到了UserDetailsService这一步,就可以自己实现了,自定义加载用户的方式。

官网demo给了一个简单明了的认证授权流程,如下:

import org.springframework.security.authentication.*;
import org.springframework.security.core.*;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;

public class AuthenticationExample {
private static AuthenticationManager am = new SampleAuthenticationManager();

public static void main(String[] args) throws Exception {
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

    while(true) {
    System.out.println("Please enter your username:");
    String name = in.readLine();
    System.out.println("Please enter your password:");
    String password = in.readLine();
    try {
        Authentication request = new UsernamePasswordAuthenticationToken(name, password);
        Authentication result = am.authenticate(request);
        SecurityContextHolder.getContext().setAuthentication(result);
        break;
    } catch(AuthenticationException e) {
        System.out.println("Authentication failed: " + e.getMessage());
    }
    }
    System.out.println("Successfully authenticated. Security context contains: " +
            SecurityContextHolder.getContext().getAuthentication());
}
}

class SampleAuthenticationManager implements AuthenticationManager {
static final List AUTHORITIES = new ArrayList();

static {
    AUTHORITIES.add(new SimpleGrantedAuthority("ROLE_USER"));
}

public Authentication authenticate(Authentication auth) throws AuthenticationException {
    if (auth.getName().equals(auth.getCredentials())) {
    return new UsernamePasswordAuthenticationToken(auth.getName(),
        auth.getCredentials(), AUTHORITIES);
    }
    throw new BadCredentialsException("Bad Credentials");
}
}

你可能感兴趣的:(Spring)