Spring Security(三):基本原理

本文介绍Spring Security基本原理,它通过自定义Filter对URL进行权限控制。以访问http://localhost:8080/hi为例,详细阐述请求处理步骤,包括经过的各类Filter及方法,如ExceptionTranslationFilter、FilterSecurityInterceptor等,还提及认证、权限验证及重定向过程。

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

Spring Security主要通过自定义的Filter对相关的URL进行权限控制,本篇文章将主要介绍它的基本原理:

继续接着上篇文章 《Spring Security(二):三分钟入门HelloWorld 》,zjj-security-demo 启动 ZjjSecurityDemoApplication 类main方法,并访问 http://localhost:8080/hi 进行观察:

访问 http://localhost:8080/hi 请求之前,在以下几个类的方法打上断点。

执行步骤


1、访问 http://localhost:8080/hi

2、首先会走到 ExceptionTranslationFilter类 doFilter()方法【原因是不满足前面几个认证过滤器的要求】

3、执行后到了拦截器 FilterSecurityInterceptor类 invoke()方法,当执行InterceptorStatusToken token = super.beforeInvocation(fi); 后就报错了

4、并返回到了ExceptionTranslationFilter,被捕获到异常信息 Access is denied (拒绝访问)

5、处理异常信息后,根据配置跳转到登录页面

6、登录之后,被UsernamePasswordAuthenticationFilter类attemptAuthentication()方法拦截

7、认证成功之后,再经过 ExceptionTranslationFilter

8、接着再到 FilterSecurityInterceptor 会验证是否有权限访问,如果有的话InterceptorStatusToken token 会返回值

9、验证成功之后,会重定向到登录页面之前的请求

 

相关源文件代码


1、FilterSecurityInterceptor

// org.springframework.security.web.access.intercept.FilterSecurityInterceptor
public void invoke(FilterInvocation fi) throws IOException, ServletException {
	if ((fi.getRequest() != null)
			&& (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
			&& observeOncePerRequest) {
		// filter already applied to this request and user wants us to observe
		// once-per-request handling, so don't re-do security checking
		fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
	}
	else {
		// first time this request being called, so perform security checking
		if (fi.getRequest() != null) {
			fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
		}

		InterceptorStatusToken token = super.beforeInvocation(fi);

		try {
			fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
		}
		finally {
			super.finallyInvocation(token);
		}

		super.afterInvocation(token, null);
	}
}

2、 ExceptionTranslationFilter

// org.springframework.security.web.access.ExceptionTranslationFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
	HttpServletRequest request = (HttpServletRequest) req;
	HttpServletResponse response = (HttpServletResponse) res;

	try {
		chain.doFilter(request, response);

		logger.debug("Chain processed normally");
	}
	catch (IOException ex) {
		throw ex;
	}
	catch (Exception ex) {
		// Try to extract a SpringSecurityException from the stacktrace
		Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
		RuntimeException ase = (AuthenticationException) throwableAnalyzer
				.getFirstThrowableOfType(AuthenticationException.class, causeChain);

		if (ase == null) {
			ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(
					AccessDeniedException.class, causeChain);
		}

		if (ase != null) {
			handleSpringSecurityException(request, response, chain, ase);
		}
		else {
			// Rethrow ServletExceptions and RuntimeExceptions as-is
			if (ex instanceof ServletException) {
				throw (ServletException) ex;
			}
			else if (ex instanceof RuntimeException) {
				throw (RuntimeException) ex;
			}

			// Wrap other Exceptions. This shouldn't actually happen
			// as we've already covered all the possibilities for doFilter
			throw new RuntimeException(ex);
		}
	}
}

3、 UsernamePasswordAuthenticationFilter

// org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
public Authentication attemptAuthentication(HttpServletRequest request,
		HttpServletResponse response) throws AuthenticationException {
	if (postOnly && !request.getMethod().equals("POST")) {
		throw new AuthenticationServiceException(
				"Authentication method not supported: " + request.getMethod());
	}

	String username = obtainUsername(request);
	String password = obtainPassword(request);

	if (username == null) {
		username = "";
	}

	if (password == null) {
		password = "";
	}

	username = username.trim();

	UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
			username, password);

	// Allow subclasses to set the "details" property
	setDetails(request, authRequest);

	return this.getAuthenticationManager().authenticate(authRequest);
}

4、 BasicAuthenticationFilter

// org.springframework.security.web.authentication.www.BasicAuthenticationFilter
protected void doFilterInternal(HttpServletRequest request,
		HttpServletResponse response, FilterChain chain)
				throws IOException, ServletException {
	final boolean debug = this.logger.isDebugEnabled();

	String header = request.getHeader("Authorization");

	if (header == null || !header.startsWith("Basic ")) {
		chain.doFilter(request, response);
		return;
	}

	try {
		String[] tokens = extractAndDecodeHeader(header, request);
		assert tokens.length == 2;

		String username = tokens[0];

		if (debug) {
			this.logger
					.debug("Basic Authentication Authorization header found for user '"
							+ username + "'");
		}

		if (authenticationIsRequired(username)) {
			UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
					username, tokens[1]);
			authRequest.setDetails(
					this.authenticationDetailsSource.buildDetails(request));
			Authentication authResult = this.authenticationManager
					.authenticate(authRequest);

			if (debug) {
				this.logger.debug("Authentication success: " + authResult);
			}

			SecurityContextHolder.getContext().setAuthentication(authResult);

			this.rememberMeServices.loginSuccess(request, response, authResult);

			onSuccessfulAuthentication(request, response, authResult);
		}

	}
	catch (AuthenticationException failed) {
		SecurityContextHolder.clearContext();

		if (debug) {
			this.logger.debug("Authentication request for failed: " + failed);
		}

		this.rememberMeServices.loginFail(request, response);

		onUnsuccessfulAuthentication(request, response, failed);

		if (this.ignoreFailure) {
			chain.doFilter(request, response);
		}
		else {
			this.authenticationEntryPoint.commence(request, response, failed);
		}

		return;
	}

	chain.doFilter(request, response);
}

源码下载
Spring Security 码云目录一览

更多内容请关注我的个人博客:http://www.zjjfx68.com/ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值