栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

解决Spring AOP 在RememberMeServices 导致的NullPointerException

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

解决Spring AOP 在RememberMeServices 导致的NullPointerException

廖雪峰:AOP避坑指南
AbstractRemembermeService logger为null导致登录失败
透过现象看原理:详解Spring中Bean的this调用导致AOP失效的原因
揭秘 Spring AOP 失效的罪因,看了都说好!
Spring AOP出现NullPointerException

原因是 AbstractRememberMeServices 有一个被外部调用的final方法,cglib无法代理final方法导致的,而这个方法又使用了this+类变量,而生成cglib代理类不会初始化类变量,所以直接报 NullPointerException

@Override
public final void loginSuccess(HttpServletRequest request, HttpServletResponse response,
      Authentication successfulAuthentication) {
   if (!rememberMeRequested(request, this.parameter)) {
      this.logger.debug("Remember-me login not requested.");
      return;
   }
   onLoginSuccess(request, response, successfulAuthentication);
}

解决办法是添加一个init()方法,通过反射获取类变量并赋值

public class LogRememberMeServices extends PersistentTokenbasedRememberMeServices {
    @Autowired
    private LogRememberMeServices mySelf;

    public LogRememberMeServices(String key, UserDetailsService userDetailsService, PersistentTokenRepository tokenRepository) {
        super(key, userDetailsService, tokenRepository);
    }


    
    public final void init() throws NoSuchFieldException, IllegalAccessException {
        Field parameter = AbstractRememberMeServices.class.getDeclaredField("parameter");
        parameter.setAccessible(true);
        if(parameter.get(this) ==null){
            parameter.set(this,getParameter());
        }
        Field logger = AbstractRememberMeServices.class.getDeclaredField("logger");
        logger.setAccessible(true);
        if(logger.get(this) ==null){
            logger.set(this, LogFactory.getLog(AbstractRememberMeServices.class));
        }
        Field userDetailsChecker = AbstractRememberMeServices.class.getDeclaredField("userDetailsChecker");
        userDetailsChecker.setAccessible(true);
        if(userDetailsChecker.get(this) ==null){
            userDetailsChecker.set(this, new AccountStatusUserDetailsChecker());
        }
    }


    @Override
    protected UserDetails processAutoLogincookie(String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) {
        return mySelf.AutoLogincookie(cookieTokens, request, response);
    }
    
    @Logging(value = LoginLog.class)
    public UserDetails AutoLogincookie(String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
      return super.processAutoLogincookie(cookieTokens, request, response);
    }
}

然后在手动执行一次init()方法即可

    @Bean
    public LogRememberMeServices rememberMeServices(UserDetailsService userDetailsService, PersistentTokenRepository persistentTokenRepository) {
        LogRememberMeServices rememberMeServices = new LogRememberMeServices(
                REMEMBER_ME_KEY, userDetailsService, persistentTokenRepository);
        rememberMeServices.setTokenValiditySeconds(60*60*24*30);
        return rememberMeServices;
    }

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, LoginLimitService loginLimitService
            , CaptchaService captchaService, RememberMeServices rememberMeServices
            , SocialService socialService, AuthenticationFailureHandler authenticationFailureHandler) throws Exception {
        //填充cglib代理的一些字段,弥补cglib代理类不能代理{@link AbstractRememberMeServices#loginSuccess}方法导致的缺陷
        if(rememberMeServices instanceof LogRememberMeServices){
            ((LogRememberMeServices)rememberMeServices).init();
        }
        return http
                .authorizeRequests(authorizeRequests ->
                        authorizeRequests
                                .antMatchers("/login/**").permitAll()
                                .anyRequest().authenticated()
                )
                .rememberMe()
                .key(REMEMBER_ME_KEY)
                .rememberMeServices(rememberMeServices).and()
                .cors().and()
                .csrf(t -> t.ignoringAntMatchers("/captcha/**"))
                .formLogin(Customizer.withDefaults())
                .logout(Customizer.withDefaults())
                .headers().frameOptions().disable().and()
                .build();

    }
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/306352.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号