Spring Security修炼手册(三)————Security个性化认证流程

    在第二章节,我们主要学习了Security的自定义认证的实现和认证的流程,同时也存在一些问题,比如我们想在用户认证成功或失败后记录日志等相关操作,我们怎么办呢?别担心,Security已经为我们想好了,我们只需要实现其提供的接口并配置即可。

一、 自定义登录成功处理  

    AuthenticationSuccessHandle是Security提供的认证成功处理器接口,代码如下:

    现在我们具体实现以下,比如我们在用户认证成功后,打印一段某某用户在什么时间登录成功的日志,并返回给前端页面当前用户的认证信息:

import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;

@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
		
	private Logger logger = LoggerFactory.getLogger(getClass());
	@Autowired
	private ObjectMapper objectMapper;
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
			Authentication authentication) throws IOException, ServletException {
		// TODO Auto-generated method stub
		logger.info(authentication.getName()+"在"+new Date()+"登录成功");
		response.setContentType("application/json;charset=UTF-8");
		response.getWriter().write(objectMapper.writeValueAsString(authentication));

	}

}

    然后我们需要将认证成功处理器在SecurityConfig中配置一下:

启动项目,再次登录,web界面显示如下:

     我们可以看到Authentication中有当前用户的权限、Session、名字等等信息,Authentication就是认证成功后Security为我们提供的一个封装,里面具体都有什么属性,感兴趣的朋友可以具体去看这个接口,这里不做详述。

        后台Log输出如下:

二、 自定义认证失败处理  

        通过名字大家应该可以猜出Security提供的失败处理器的名字叫做:AuthenticationFailureHandler,

这个处理器和成功处理器还是有区别的,成功处理器方法中最后一个参数是封装好的用户认证信息,而失败处理器最后一个参数是在UserDetailServer中所捕获到的异常信息。可以看下我实现的逻辑:打印抛出的异常信息。

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {

	private Logger logger = LoggerFactory.getLogger(getClass());
	/* (non-Javadoc)
	 * @see org.springframework.security.web.authentication.AuthenticationFailureHandler#onAuthenticationFailure(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.springframework.security.core.AuthenticationException)
	 */
	@Override
	public void onAuthenticationFailure(HttpServletRequest arg0, HttpServletResponse response, AuthenticationException authentication)
			throws IOException, ServletException {
		
		logger.info(authentication.getMessage()+"---登录失败");
		response.setHeader("content-type","text/html;charset=UTF-8");
		response.getWriter().println("<script>alert('"+ authentication.getMessage()+"');</script>");

	}

}

同样的在SecurityConfig中配置失败处理器,再次登录:

    可以看到打印出的异常信息为“坏的凭证”,实际上这个是Security中默认的异常信息,我们跑一遍源码,来看一看Security是怎么对比我们在表单输入的信息与UserDetailServer中(数据库)的对比,以及异常的处理的。

    任意输入用户名,密码,点击登录,我们还是先来到了UsernamePasswordAuthenticationFilter中。

我们继续往下走,一直走到我们的UserDetailServer的实现中。

    走到User的时候我们直接走进去,进入到DaoAuthenticationProvider,在这里会对User进行初步的检查,我们看右上角的堆栈信息,可以看到loadedUser中我画红框值为true的属性,实际上这几个是框架默认填充的值,这因为我们只调用了三个参数的构造器,如果我们调用七个参数的构造器,就可以根据我们自己的业务逻辑去进行true或false的判断,抛出相应的异常信息。

当我们继续走,下面就开始对User进行用户名,密码,是否可用等等各种检查,如果发现出现问题,则抛出异常信息。

由于我填写的密码是错误的,所以在检查过程中,抛出以下异常“坏的凭证”,认证是是失败的,那个false的含义是我是第一次经过认证的。

而这里抛出的异常信息“坏的凭证”则会被我们的失败处理器所捕获,这就是异常信息捕获的全部过程。

三、自定义异常信息

    第二节我们详细的讲解了异常信息的捕获流程,但是在我们的实际业务需求中,异常信息可能不是“坏的凭证”这样的字符串,如果我想自定义信息,怎么办呢?也很简单,请看代码:

    我将UserDetailServer稍作修改。

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.Component;

@Component
public class MyUserDetailsServer implements UserDetailsService {

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// TODO Auto-generated method stub
		if("admin".equals(username)){
			
			throw new RuntimeException("admin禁止登录");
		}
		return new User(username, "$2a$10$ofPkBDUezOJp6Sik63Q/0.QlU8a1itEyzldjSXqfn2nDPqXjN0Ljm", AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
	}

}

我这里只是举一个例子,具体你们的业务需求是怎样的,自己实现就好。重启项目,使用admin登录,效果如下。

猜你喜欢

转载自my.oschina.net/u/3637243/blog/1821600