Spring Security系列-Spring Security运行机制分析(一)

前言

Spring Security作为Spring家族里的一个重要成员,目的是对用户认证和鉴权进行处理。用过Spring Security的人应该会觉得配置很多,运行机制很复杂,难以驾驭。下面我们从一个简单的小程序开始,慢慢的揭开Spring Security的面纱。

从一个小程序开始

下面是一个来自Spring官方的样例,代码很简单。真实情况会更加复杂,我们先从简单的开始。

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<GrantedAuthority> 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");
    }
}

运行后,如果输入的用户名和密码一样,则验证通过。运行结果如下:

Please enter your username:
admin
Please enter your password:
123456
Authentication failed: Bad Credentials
Please enter your username:
admin
Please enter your password:
admin
Successfully authenticated. Security context contains: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@441d0230: Principal: admin; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER

AuthenticationManager和Authentication

在上面的小程序中,使用到SampleAuthenticationManager类,实现了AuthenticationManager接口。AuthenticationManager接口中只定义了一个authenticate方法,该方法的输入和输出类型都是Authentication。Authentication接口,存储了用户输入的用户名(Principal.getName)、密码(getCredentials)和权限(getAuthorities)等认证相关信息。

public interface AuthenticationManager {
	Authentication authenticate(Authentication authentication)
			throws AuthenticationException;
}

此处的验证是判断用户名和密码是否相等,而真实环境会拿用户输入的用户和密码,与数据库中的用户名和密码进行比对,如果一样,则验证通过。当验证通过时,就返回一个新实例化的authentication,包含了用户名、密码和权限,并存放到SecurityContext中;当验证失败,则抛出一个BadCredentialsException异常,表示认证失败。

SecurityContext和SecurityContextHolder

SecurityContext是用来提供和存放Authentication认证信息的。SecurityContextHolder是用来提供和存放SecurityContext的。SecurityContextHolder是提供了context的get和set的静态方法,这样我们就可以后程序非常方便的获取SecurityContext,再获取SecurityContext中的Authentication,也就是认证信息。
需要注意的是,SecurityContext有三种存放策略,默认使用ThreadLocal方式,也就是说对于每个线程都会有一个SecurityContext副本。

final class ThreadLocalSecurityContextHolderStrategy implements
		SecurityContextHolderStrategy {
	private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
	
	public void clearContext() {
		contextHolder.remove();
	}

	public SecurityContext getContext() {
		SecurityContext ctx = contextHolder.get();

		if (ctx == null) {
			ctx = createEmptyContext();
			contextHolder.set(ctx);
		}

		return ctx;
	}

	public void setContext(SecurityContext context) {
		Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
		contextHolder.set(context);
	}

	public SecurityContext createEmptyContext() {
		return new SecurityContextImpl();
	}
}

过程

从一个小程序开始,可以了解到认证过程经过了以下几步

  • 用户输入用户名和密码
  • 生成一个Authentication
  • AuthenticationManager对Authentication进行判断是否认证通过
  • 如果认证失败,抛出一个AuthenticationException
  • 如果认证成功,则返回一个新的Authentication,它包含了用户名、密码和权限
  • 将新的认证信息,存放到SecurityContext中

源代码

github源代码:https://github.com/camellibby/security-demo

结语

上面是一个简单的小程序,真实的情况会更加复杂,后面的文章在此基础上演变,逐步靠近真实的情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值