欢迎大家去我的个人网站踩踩 点这里哦
一、前言
上篇文章,我们简单讲了一下单认证方式的配置,以及各个spring security配置文件的作用
https://blog.csdn.net/qq_36521507/article/details/103365805
本篇则讲一下多种认证方式的配置
二、多认证
1、自定义认证过滤器
由上篇文章,我们知道了要配置登录认证,需要先自定义一个过滤器,我们参考默认过滤器自定义一个
public class CitictAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
private boolean postOnly = true;
public CitictAuthenticationProcessingFilter() {
super(new AntPathRequestMatcher("/citict/doLogin", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
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();
CitictAuthenticationToken authRequest = new CitictAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
2、自定义AuthenticationToken实现类
在此之前,我们需要先自定义一个AuthenticationToken的实现类,用来封装认证参数,以及绑定认证器
public class CitictAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = -6437322217156360297L;
private final Object principal;
private Object credentials;
/**
* This constructor can be safely used by any code that wishes to create a
* <code>UsernamePasswordAuthenticationToken</code>, as the {@link #isAuthenticated()}
* will return <code>false</code>.
*
*/
public CitictAuthenticationToken(Object principal, Object credentials) {
super(null);
this.principal = principal;
this.credentials = credentials;
setAuthenticated(false);
}
/**
* This constructor should only be used by <code>AuthenticationManager</code> or
* <code>AuthenticationProvider</code> implementations that are satisfied with
* producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)
* authentication token.
*
* @param principal
* @param credentials
* @param authorities
*/
public CitictAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true); // must use super, as we override
}
@Override
public Object getCredentials() {
return this.credentials;
}
@Override
public Object getPrincipal() {
return this.principal;
}
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException(
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
credentials = null;
}
}
3、自定义一个认证处理器AuthenticationProvider
@Slf4j
@Component
public class CitictAuthenticationProvider implements AuthenticationProvider {
/**
* 注入我们自己定义的用户信息获取对象
*/
@Autowired
private UserDetailsService userDetailService;
private UserDetailsChecker userDetailsChecker = new MyPreAuthenticationChecks();
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// TODO Auto-generated method stub
String userName = authentication.getName();// 这个获取表单输入中返回的用户名;
String password = (String)authentication.getCredentials();// 这个是表单中输入的密码;
MyWebAuthenticationDetails details = (MyWebAuthenticationDetails)authentication.getDetails();
// 这里构建来判断用户是否存在和密码是否正确
UserInfo userInfo = (UserInfo)userDetailService.loadUserByUsername(userName); // 这里调用我们的自己写的获取用户的方法;
userDetailsChecker.check(userInfo);
/**
* 登录认证
*/
try {
LoginUtil.login(userName, password);
} catch (Exception e) {
e.printStackTrace();
throw new BadCredentialsException(e.getMessage());
}
Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
// 构建返回的用户登录成功的token
return new CitictAuthenticationToken(userInfo, password, authorities);
}
@Override
public boolean supports(Class<?> authentication) {
/**
* providerManager会遍历所有
* securityconfig中注册的provider集合
* 根据此方法返回true或false来决定由哪个provider
* 去校验请求过来的authentication
*/
return (CitictAuthenticationToken.class
.isAssignableFrom(authentication));
}
}
注意supports方法,只有这样判断,才会在自定义过滤器调用认证管理器认证时,只调用CitictAuthenticationProvider我们自定义的认证方法,排除其他认证器,具体原因参考上篇文章。
4、最后看下spring security配置文件
@Configuration
@Slf4j
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyAuthenticationProvider defaultProvider; //默认本地用户名密码登录AuthenticationProvider
@Autowired
private CitictAuthenticationProvider citictProvider; //自定义登录AuthenticationProvider
@Autowired
private AuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Autowired
private AuthenticationFailureHandler myAuthenticationFailHander;
@Autowired
private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> myAuthenticationDetailsSource;
@Autowired
private ValidateCodeFilter validateCodeFilter;
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Override
protected void configure(HttpSecurity http) throws Exception {
//super.configure(http);
http.cors();
http.csrf().disable();
//拦截器需要在此注册
http.addFilterBefore(validateCodeFilter, AbstractPreAuthenticatedProcessingFilter.class);
http.addFilterBefore(citictAuthenticationProcessingFilter(), AbstractPreAuthenticatedProcessingFilter.class);
/**
* 此处配置的过滤器拦截地址、认证成功失败处理器,需要在下面过滤器单独配置
*/
http.formLogin().loginPage("/toLogin")
.failureUrl("/loginError")
//.loginProcessingUrl("/doLogin") //改为在过滤器处配置
//.authenticationDetailsSource(myAuthenticationDetailsSource) //改为在过滤器处配置
//.successHandler(myAuthenticationSuccessHandler) //改为在过滤器处配置
//.failureHandler(myAuthenticationFailHander) //改为在过滤器处配置
.permitAll() //表单登录,permitAll()表示这个不需要验证 登录页面,登录失败页面
.and()
.logout().permitAll().invalidateHttpSession(true)
.deleteCookies("JSESSIONID").logoutSuccessHandler(logoutSuccessHandler())
.and()
.authorizeRequests()
.antMatchers("/swagger-ui.html","/webjars/**","/v2/**","/swagger-resources/**","/favicon.ico","/css/**","/common/**","/js/**","/images/**",
"/captcha.jpg","/login","/doLogin","/doCitictLogin","/loginError","/getAllTenant","/sessionExpired","/sessionInvalid","/code/*").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().invalidSessionUrl("/sessionInvalid")
.maximumSessions(10)
// 当达到最大值时,是否保留已经登录的用户
.maxSessionsPreventsLogin(false)
// 当达到最大值时,旧用户被踢出后的操作
//.expiredSessionStrategy(customExpiredSessionStrategy());
//在上一句过期策略里配置
.expiredUrl("/sessionExpired");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
//将两个自定义认证器都注册
auth.authenticationProvider(defaultProvider);
auth.authenticationProvider(citictProvider);
}
//这个必须重写,才能使用AuthenticationManager,在成员变量注入进来,再注入过滤器中
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
//下面就是默认的过滤器UsernamePasswordAuthenticationFilter
//配置一下拦截地址、认证成功失败处理器、authenticationManager
/**
* 默认用户名密码认证过滤器
* @Author guomh 2019/12/02
* @return
*/
@Bean
public UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() {
UsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
filter.setAuthenticationDetailsSource(myAuthenticationDetailsSource);
filter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
filter.setAuthenticationFailureHandler(myAuthenticationFailHander);
filter.setFilterProcessesUrl("/doLogin");
return filter;
}
//下面就是自定义的过滤器,配置一下拦截地址、认证成功失败处理器、authenticationManager
//如果还有其他认证过滤器,则再这样写一个
/**
* 自定义登录过滤器
* @Author guomh 2019/12/02
* @return
*/
@Bean
public CitictAuthenticationProcessingFilter citictAuthenticationProcessingFilter() {
CitictAuthenticationProcessingFilter filter = new CitictAuthenticationProcessingFilter();
filter.setAuthenticationManager(authenticationManager);
filter.setAuthenticationDetailsSource(myAuthenticationDetailsSource);
filter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
filter.setAuthenticationFailureHandler(myAuthenticationFailHander);
filter.setFilterProcessesUrl("/doCitictLogin");
return filter;
}
@Bean
public LogoutSuccessHandler logoutSuccessHandler() { //登出处理
return new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
try {
UserInfo user = (UserInfo) authentication.getPrincipal();
log.info("USER : " + user.getUsername() + " LOGOUT SUCCESS ! ");
} catch (Exception e) {
log.info("LOGOUT EXCEPTION , e : " + e.getMessage());
}
httpServletResponse.sendRedirect("/toLogin");
}
};
}
@Bean
public SessionInformationExpiredStrategy customExpiredSessionStrategy() {
return new SessionInformationExpiredStrategy() {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Override
public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
// 如果是跳转html页面,url代表跳转的地址
redirectStrategy.sendRedirect(event.getRequest(), event.getResponse(), "/sessionExpired");
}
};
}
@Bean("sessionStrategy")
public SessionStrategy sessionStrategy() {
SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
return sessionStrategy;
}
}
三、总结
这样配置,易于扩展,如果我们要再加一个手机登录认证器,我们只需要写
MobileAuthenticationProcessingFilter、MobileAuthenticationToken、MobileAuthenticationProvider
再配置文件中注册拦截器bean, MobileAuthenticationProcessingFilter
加入拦截器链,http.addFilterBefore(MobileAuthenticationProcessingFilter(), AbstractPreAuthenticatedProcessingFilter.class);
注册认证器 auth.authenticationProvider(mobileProvider) 即可
每个拦截器都可根据需要自定义配置。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/111318.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...