filter请求过滤器可以帮助我们进行HttpServletRequest请求和HttpServletResponse响应的过滤
在自定义的Filter过滤器中我们可以对我们的请求进行过滤, 同时也可以对返回的相应进行处理,在方法中实现我们自定义的业务逻辑处理,这是很常见的需求,下面说说spring security框架中怎么是怎么搭配自定义Filter过滤器来使用的。
package com.kkarma.filter;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class MyFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("myFilter 被执行了...");
filterChain.doFilter(request, response);
}
}
解释下为什么继承自OncePerRequestFilter这个类,OncePerRequestFilter是Spring Boot里面的一个过滤器抽象类,这个类的作用就是用于继承实现并在每次请求时只执行一次过滤,如果想知道详细的实现原理,可以自行翻阅源码, 实现也比较简单, 一看就懂。
filter的实现使用了设计模式中的责任链模式,在添加自定义的filter时,可以有很多方时,可以指定将自定义的Filter添加到某个指定的Filter之前或者之后,具体怎么实现看用户自己的选择, 如果对于方法不了解, 可以进入到方法里面,看看方法的注释,说的都比较清楚。
注入自定义Filter的bean对象
将自定义Filter的bean对象添加到过滤器链中的用户登录认证校验过滤器之前
完整配置类代码
package com.kkarma.config;
import com.kkarma.filter.MyFilter;
import com.kkarma.handler.CustomAccessDeniedHandler;
import com.kkarma.web.service.impl.MyUserDetailsServiceImpl;
import org.apache.coyote.Adapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
/**
* 自定义Filter
*/
@Autowired
private MyFilter myFilter;
@Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
/**
* 自定义用户认证逻辑
*/
@Autowired
private UserDetailsService userDetailsService;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
/**
* 配置csrf开启关闭,默认是开启的
*/
http.csrf().disable();
/**
* 表单登录相关的配置
*/
// http.formLogin()
// 前端登录表单用户名别名, 从参数user中获取username参数取值
// .usernameParameter("user")
// 前端登录表单密码别名, 从参数passwd中获取password参数取值
// .passwordParameter("passwd")
// 当http请求的url是/login时,进行我们自定义的登录逻辑
// .loginProcessingUrl("/login")
// 自定义登录的前端控制器
// .loginPage("/showLogin")
// 设置登录成功的跳转链接
// .successForwardUrl("/home");
// 通过successHandler处理器进行登录成功之后的逻辑处理
// .successHandler(new AuthenticationSuccessHandler() {
// @Override
// public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
// System.out.println("登录成功,页面即将跳转...");
// Collection extends GrantedAuthority> authorities = authentication.getAuthorities();
// authorities.forEach(System.out::println);
// response.sendRedirect("/home");
// }
// })
// 设置登录失败的跳转链接
// .failureForwardUrl("/errPage");
// 通过failureHandler处理器进行登录失败之后的逻辑处理
// .failureHandler(new AuthenticationFailureHandler() {
// @Override
// public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
// e.printStackTrace();
// System.out.println("登录失败,页面即将跳转到默认失败页...");
// response.sendRedirect("/errPage");
// }
// })
// .and()
// 用户退出登录处理配置
http
// 403权限不足异常使用自定义403处理器处理
.exceptionHandling().accessDeniedHandler(accessDeniedHandler)
.and()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.logout()
// 用户退出成功之后的跳转链接
.logoutSuccessUrl("/showLogin")
// 设置用户退出的url
.logoutUrl("/logout")
// 自定义用户的退出逻辑处理器, 可以处理认证信息、session、cookies等信息
.addLogoutHandler(new LogoutHandler() {
@Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
// 这里可以自定义用户退出的处理逻辑
System.out.println("退出成功...");
}
})
// 是否清除认证状态,默认为true
.clearAuthentication(true)
// 是否销毁HttpSession对象,默认为true
.invalidateHttpSession(true);
/**
* http请求是否要登录认证配置
*/
http.authorizeRequests()
// 允许GET请求登录页面匿名访问
.antMatchers(HttpMethod.GET, "/showLogin", "/errPage", "/captchaImage").anonymous()
.antMatchers(HttpMethod.POST, "/login").anonymous()
// 匹配所有名称以demo结尾的js并允许匿名访问
.regexMatchers(HttpMethod.GET, ".+demo[.]js").anonymous()
// 用户具有admin角色时允许访问/role
.antMatchers(HttpMethod.GET, "/role").hasRole("admin")
// 用户具有system:user权限时允许访问/role
.antMatchers(HttpMethod.GET, "/role").hasAuthority("system:user")
// 所有的静态资源允许匿名访问
.antMatchers(
"/css/**",
"/js/**",
"/images/**",
"/fonts/**",
"/favicon.ico"
).anonymous()
// 其他所有的请求都需要登录认证
.anyRequest().authenticated()
.and()
// 意思就是在登录认证校验过滤器执行之前先进行自定义过滤器myFilter的执行
.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class)
;
}
/**
* 身份认证接口
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
/**
* 在内存中预置两个演示用户admin和common
*/
// @Override
// protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//
// auth.inMemoryAuthentication()
// .withUser("admin")
// .password(bCryptPasswordEncoder().encode("123456"))
// .roles("admin", "superAdmin")
// .authorities(AuthorityUtils.commaSeparatedStringToAuthorityList("system,sytem:user,system:user:list"))
// .and()
// .withUser("common")
// .password(bCryptPasswordEncoder().encode("123456"))
// .roles("common")
// .authorities(AuthorityUtils.commaSeparatedStringToAuthorityList("good,good:cate,good:cate:list"));
// }
/**
* 强散列哈希加密实现
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder()
{
return new BCryptPasswordEncoder();
}
}
启动项目, 看看filter是否生效,随便发送一个请求,看到我们定义的myFilter被执行了,我们可以在这个过滤器里面添加自己的逻辑实现