在第二章节,我们主要学习了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登录,效果如下。