文章目录
SpringSecurity功能介绍和原理分析
一、基本原理
基于servlet的Filter来实现的!!
绿色部分的各个过滤器(如 UsernamePasswordAuthenticationFilter),主要完成SpringSecurity的各种认证工作,如Form表单认证(登录页)和 http basic认证等
黄色的 FilterSecurityInterceptor 是整个过滤器链中的最后一环,最终由它决定当前请求能否到达最后请求的资源
蓝色的ExceptionTranslationFilter过滤器,用于捕获FilterSecurityInterceptor抛出的各种异常,进行对应的处理
默认的过滤器链:
二、认证流程详解
认证成功后认证结果在多个请求之间实现共享
SecurityContextPersistenceFilter实现认证结果共享
UsernamePasswordAuthenticationFilter: 用户名和密码认证的过滤器
调用父类的AbstractAuthenticationProcessingFilter的doFilter进行认证:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
。。。。。
authResult = attemptAuthentication(request, response);//认证
。。。。
}
catch (InternalAuthenticationServiceException failed) {
logger.error(
"An internal error occurred while trying to authenticate the user.",
failed);
unsuccessfulAuthentication(request, response, failed);
return;
}
catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(request, response, failed);
return;
}
successfulAuthentication(request, response, chain, authResult);
}
UsernamePasswordAuthenticationFilter:
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
....
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
....
return this.getAuthenticationManager().authenticate(authRequest);//交由认证管理器进行认证操作
}
认证管理器:
ProviderManager(默认的认证管理器)
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
。。。。
//遍历所有的认证提供者,找一个合适的认证提供者来处理请求
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
try {
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
}
。。。。
}
DaoAuthenticationProvider:
调用父类AbstractUserDetailsAuthenticationProvider的authenticate进行认证
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.onlySupports",
"Only UsernamePasswordAuthenticationToken is supported"));
// Determine username
String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
: authentication.getName();
boolean cacheWasUsed = true;
UserDetails user = this.userCache.getUserFromCache(username);
if (user == null) {
try {
//获取真实的用户信息
user = retrieveUser(username,
(UsernamePasswordAuthenticationToken) authentication);
}
}
try {
......
//用户认证
additionalAuthenticationChecks(user,
(UsernamePasswordAuthenticationToken) authentication);
}
return createSuccessAuthentication(principalToReturn, authentication, user);
}
DaoAuthenticationProvider:
retrieveUser:
protected final UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
try {
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
return loadedUser;
}
}
additionalAuthenticationChecks:
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
String presentedPassword = authentication.getCredentials().toString();
//密码比对
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}
三、个性化用户认证流程
需求:修改登录时的默认表单请求为ajax异步请求!!!
login(){
axios.post('/login.do','username='+this.username+'&password='+this.password+"&imageCode="+this.imageCode+"&imageCode="+this.imageCode
+'&rememberMe='+this.rememberMe).then((res)=>{
if(res.data.flag){
window.location.href="/pages/main.html";
}else{
this.msg=res.data.message;
}
});
}
1、修改登录成功后返回json数据
接口及常用实现类
AuthenticationSuccessHandler
--SavedRequestAwareAuthenticationSuccessHandler 默认
--ForwardAuthenticationSuccessHandler
--SimpleUrlAuthenticationSuccessHandler
自定义认证成功处理器
public class AuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
private String type;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
logger.info("登录成功");
if ("json".equalsIgnoreCase(this.type) || "json".equalsIgnoreCase(request.getParameter("type"))) {
//返回json格式数据
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"flag\":true,\"message\":\"登录成功\"}");
} else {
//默认的页面跳转
super.onAuthenticationSuccess(request, response, authentication);
}
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
配置
<security:form-login
login-page="/login.jsp"
default-target-url="/index.html"
authentication-failure-url="/login.jsp"
authentication-success-handler-ref="authenticationSuccessHandler"/>
<bean id="authenticationSuccessHandler" class="com.itheima.AuthenticationSuccessHandler">
<property name="type" value="json"></property>
</bean>
2、修改处理登录失败后返回json数据
接口及常用实现类
AuthenticationFailureHandler
--SimpleUrlAuthenticationFailureHandler 默认
--DelegatingAuthenticationFailureHandler
--ForwardAuthenticationFailureHandler
自定义认证失败处理器
public