spring-security 5.0.3 ajax登录时校验验证码是否正确的2种方式

第一种方式 直接在Controller 中校验


@RestController
public class LoginController extends AbstractController {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private HttpServletRequest request;


    /**
     * 登录
     * @param userAccount  登录账号
     * @param userPwd   登录密码
     * @param verifyCode  验证码
     * @return
     */
    @PostMapping("login/enter")
    public RestfulVo login(String userAccount, String userPwd,String verifyCode ){
        System.out.println("进入了 controller 中的登录方法 ............. ");
        if(StringUtils.isBlank(verifyCode) || !verifyCode.trim().equals("1234")){
            return ResultUtil.fail("验证码错误.");
        }
        //登录 身份认证
        // 这句代码会自动执行咱们自定义的 "MyUserDetailService.java" 身份认证类
      //1: 将用户名和密码封装成UsernamePasswordAuthenticationToken  new UsernamePasswordAuthenticationToken(userAccount, userPwd)
        //2: 将UsernamePasswordAuthenticationToken传给AuthenticationManager进行身份认证   authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userAccount, userPwd));
        //3: 认证完毕,返回一个认证后的身份: Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userAccount, userPwd));
        //4: 认证后,存储到SecurityContext里   SecurityContext securityContext = SecurityContextHolder.getContext();securityContext.setAuthentication(authentication);


        //UsernamePasswordAuthenticationToken继承AbstractAuthenticationToken实现Authentication
        //当在页面中输入用户名和密码之后首先会进入到UsernamePasswordAuthenticationToken验证(Authentication),注意用户名和登录名都是页面传来的值
        //然后生成的Authentication会被交由AuthenticationManager来进行管理
        //而AuthenticationManager管理一系列的AuthenticationProvider,
        //而每一个Provider都会通UserDetailsService和UserDetail来返回一个
        //以UsernamePasswordAuthenticationToken实现的带用户名和密码以及权限的Authentication
        try {
            Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userAccount, userPwd));
            //将身份 存储到SecurityContext里
            SecurityContext securityContext = SecurityContextHolder.getContext();
            securityContext.setAuthentication(authentication);
            request.getSession().setAttribute("SPRING_SECURITY_CONTEXT", securityContext); // 这个非常重要,否则验证后将无法登陆
            return ResultUtil.success("登录成功.");
        }catch (AuthenticationException e){
            e.printStackTrace();
            return ResultUtil.fail("用户或者密码错误.");
        }
    }

}

WebSecurityConfig 配置类中配置 需要把.loginProcessingUrl("/auth/v1/api/login/enter") 指向controller中的方法地址

 @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("不需要认证的url:"+antMatchers);
        //super.configure(http);
        //关闭csrf验证
        http.csrf().disable()
                // 基于token,所以不需要session  如果基于session 则表使用这段代码
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                //对请求进行认证  url认证配置顺序为:1.先配置放行不需要认证的 permitAll() 2.然后配置 需要特定权限的 hasRole() 3.最后配置 anyRequest().authenticated()
                .authorizeRequests()
                // 所有 /oauth/v1/api/login/ 请求的都放行 不做认证即不需要登录即可访问
                .antMatchers(antMatchers.split(",")).permitAll()
                //.antMatchers("/auth/v1/api/login/**","/auth/v1/api/module/tree/**","/auth/v1/api/grid/**").permitAll()
                // 对于获取token的rest api要允许匿名访问
                .antMatchers("oauth/**").permitAll()
                // 其他请求都需要进行认证,认证通过够才能访问   待考证:如果使用重定向 httpServletRequest.getRequestDispatcher(url).forward(httpServletRequest,httpServletResponse); 重定向跳转的url不会被拦截(即在这里配置了重定向的url需要特定权限认证不起效),但是如果在Controller 方法上配置了方法级的权限则会进行拦截
                .anyRequest().authenticated()
                .and().exceptionHandling()
                // 认证配置当用户请求了一个受保护的资源,但是用户没有通过登录认证,则抛出登录认证异常,MyAuthenticationEntryPointHandler类中commence()就会调用
                .authenticationEntryPoint(myAuthenticationEntryPoint())
                //用户已经通过了登录认证,在访问一个受保护的资源,但是权限不够,则抛出授权异常,MyAccessDeniedHandler类中handle()就会调用
                .accessDeniedHandler(myAccessDeniedHandler())
                .and()
                //
                .formLogin()
                // 登录url
               // .loginProcessingUrl("/auth/v1/api/login/entry")  // 此登录url 和Controller 无关系
               .loginProcessingUrl("/auth/v1/api/login/enter")  //使用自己定义的Controller 中的方法 登录会进入Controller 中的方法
                // username参数名称 后台接收前端的参数名
                .usernameParameter("userAccount")
                //登录密码参数名称 后台接收前端的参数名
                .passwordParameter("userPwd")
                //登录成功跳转路径
                .successForwardUrl("/")
                //登录失败跳转路径
                .failureUrl("/")
                //登录页面路径
                .loginPage("/")
                .permitAll()
                //登录成功后 MyAuthenticationSuccessHandler类中onAuthenticationSuccess()被调用
                .successHandler(myAuthenticationSuccessHandler())
                //登录失败后 MyAuthenticationFailureHandler 类中onAuthenticationFailure()被调用
                .failureHandler(myAuthenticationFailureHandler())
                .and()
                .logout()
                //退出系统url
                .logoutUrl("/auth/v1/api/login/logout")
                //退出系统后的url跳转
                .logoutSuccessUrl("/")
                //退出系统后的 业务处理
                .logoutSuccessHandler(myLogoutSuccessHandler())
                .permitAll()
                .invalidateHttpSession(true)
                .and()
                //登录后记住用户,下次自动登录,数据库中必须存在名为persistent_logins的表
                // 勾选Remember me登录会在PERSISTENT_LOGINS表中,生成一条记录
                .rememberMe()
                //cookie的有效期(秒为单位
                .tokenValiditySeconds(3600);
 


    }

如果出现AuthenticationManager 无法找到则 需要在WebSecurityConfig 配置类中加上如下

/**
     *  解决 无法直接注入 AuthenticationManager
     * @return
     * @throws Exception
     */
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

第二种通过继承 UsernamePasswordAuthenticationFilter

/***
 *  自定义 登录时认证
 */
@Slf4j
public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    public  MyUsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager,AuthenticationSuccessHandler successHandler,AuthenticationFailureHandler failureHandler){
        this.setFilterProcessesUrl("/auth/v1/api/login/entry");  //这句代码很重要,设置登陆的url 要和 WebSecurityConfig 配置类中的.loginProcessingUrl("/auth/v1/api/login/entry") 一致,如果不配置则无法执行 重写的attemptAuthentication 方法里面而是执行了父类UsernamePasswordAuthenticationFilter的attemptAuthentication()
        this.setAuthenticationManager(authenticationManager);   // AuthenticationManager 是必须的
        this.setAuthenticationSuccessHandler(successHandler);  //设置自定义登陆成功后的业务处理
        this.setAuthenticationFailureHandler(failureHandler); //设置自定义登陆失败后的业务处理
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        //校验验证码
        String verifyCode = request.getParameter("verifyCode");
        if(!checkValidateCode(verifyCode)){
            ResultUtil.writeJavaScript(response,ErrorCodeEnum.FAIL,"验证码错误.");
            return null;
        }
        //设置获取 username 的属性字段   js传到后台接收数据的参数名
        this.setUsernameParameter("userAccount");
        //设置获取password 的属性字段  js传到后台接收数据的参数名
        this.setPasswordParameter("userPwd");

        return super.attemptAuthentication(request, response);
    }

    /**
     * 验证 验证码是否正确
     * @param verifyCode
     * @return
     */
    private boolean checkValidateCode(String verifyCode){
        if(StringUtils.isBlank(verifyCode) || !verifyCode.trim().equals("1234")){
          return false;
        }
        return true;
    }
}

WebSecurityConfig 配置类中 需要加入自定义UsernamePasswordAuthenticationFilter替代原有Filter

@Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("不需要认证的url:"+antMatchers);
        //super.configure(http);
        //关闭csrf验证
        http.csrf().disable()
                // 基于token,所以不需要session  如果基于session 则表使用这段代码
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                //对请求进行认证  url认证配置顺序为:1.先配置放行不需要认证的 permitAll() 2.然后配置 需要特定权限的 hasRole() 3.最后配置 anyRequest().authenticated()
                .authorizeRequests()
                // 所有 /oauth/v1/api/login/ 请求的都放行 不做认证即不需要登录即可访问
                .antMatchers(antMatchers.split(",")).permitAll()
                //.antMatchers("/auth/v1/api/login/**","/auth/v1/api/module/tree/**","/auth/v1/api/grid/**").permitAll()
                // 对于获取token的rest api要允许匿名访问
                .antMatchers("oauth/**").permitAll()
                // 其他请求都需要进行认证,认证通过够才能访问   待考证:如果使用重定向 httpServletRequest.getRequestDispatcher(url).forward(httpServletRequest,httpServletResponse); 重定向跳转的url不会被拦截(即在这里配置了重定向的url需要特定权限认证不起效),但是如果在Controller 方法上配置了方法级的权限则会进行拦截
                .anyRequest().authenticated()
                .and().exceptionHandling()
                // 认证配置当用户请求了一个受保护的资源,但是用户没有通过登录认证,则抛出登录认证异常,MyAuthenticationEntryPointHandler类中commence()就会调用
                .authenticationEntryPoint(myAuthenticationEntryPoint())
                //用户已经通过了登录认证,在访问一个受保护的资源,但是权限不够,则抛出授权异常,MyAccessDeniedHandler类中handle()就会调用
                .accessDeniedHandler(myAccessDeniedHandler())
                .and()
                //
                .formLogin()
                // 登录url
                .loginProcessingUrl("/auth/v1/api/login/entry")  // 此登录url 和Controller 无关系
               // .loginProcessingUrl("/auth/v1/api/login/enter")  //使用自己定义的Controller 中的方法 登录会进入Controller 中的方法
                // username参数名称 后台接收前端的参数名
                .usernameParameter("userAccount")
                //登录密码参数名称 后台接收前端的参数名
                .passwordParameter("userPwd")
                .permitAll()
                .and()
                .logout()
                //退出系统url
                .logoutUrl("/auth/v1/api/login/logout")
                //退出系统后的url跳转
                .logoutSuccessUrl("/")
                //退出系统后的 业务处理
                .logoutSuccessHandler(myLogoutSuccessHandler())
                .permitAll()
                .invalidateHttpSession(true)
                .and()
                //登录后记住用户,下次自动登录,数据库中必须存在名为persistent_logins的表
                // 勾选Remember me登录会在PERSISTENT_LOGINS表中,生成一条记录
                .rememberMe()
                //cookie的有效期(秒为单位
                .tokenValiditySeconds(3600);
        // 加入自定义UsernamePasswordAuthenticationFilter替代原有Filter
        http.addFilterAt(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        // 禁用缓存
        http.headers().cacheControl();


    }


    /**
     * 注册  自定义登录成功 处理 bean
     * @return
     */
    @Bean
    public AuthenticationSuccessHandler myAuthenticationSuccessHandler(){
        return new MyAuthenticationSuccessHandler();
    }

    /**
     *  注册 自定义登录失败 处理 bean
     * @return
     */
    @Bean
    public AuthenticationFailureHandler myAuthenticationFailureHandler(){
        return new MyAuthenticationFailureHandler();
    }

    /**
     * 验证登录验证码
     * @return
     * @throws Exception
     */
    @Bean
    public UsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter() throws Exception {
        return new MyUsernamePasswordAuthenticationFilter(authenticationManagerBean(),myAuthenticationSuccessHandler(),myAuthenticationFailureHandler());
    }

你可能感兴趣的:(spring-security 5.0.3 ajax登录时校验验证码是否正确的2种方式)