返回顶部
首页 > 资讯 > 后端开发 > Python >SpringSecurity实现添加图片验证功能
  • 780
分享到

SpringSecurity实现添加图片验证功能

SpringSecurity添加图片验证SpringSecurity图片验证 2023-01-04 15:01:28 780人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录本章内容思路方案怎么将字符串变成图片验证码?kaptcha这么玩hutool这么玩传统WEB项目过滤器方式认证器方式总结下前后端分离项目基于过滤器方式基于认证器方式本章内容 sp

本章内容

spring security添加图片验证方式,在互联网上面有很多这种博客,都写的非常的详细了。本篇主要讲一些添加图片验证的思路。还有前后端分离方式,图片验证要怎么去处理?

  • 图片验证的思路
  • 简单的demo

思路

小白: "我们从总体流程上看图片验证在认证的哪一个阶段?"

小黑: "在获取客户输入的用户名密码那一阶段,而且要在服务器获取数据库中用户名密码之前。这是一个区间[获取请求用户名密码, 获取数据库用户名密码)

而在 Spring security中, 可以很明显的发现有两种思路。

  • 第1种思路是在拦截登录请求准备认证的那个过滤器。
  • 第2种思路是在那个过滤器背后的认证器。"

小白: "为什么是这个阶段呢? 不能是在判断密码验证之前呢?"

小黑: "你傻啊, 如果在你说的阶段, 服务器需要去数据库中获取用户信息, 这相当的浪费系统资源"

小白: "哦哦, 我错了, 让我屡屡整个流程应该是啥样"

小白: "我需要事先在后端生成一个验证码,然后通过验证码返回一张图片给前端。前端登录表单添加图片验证。用户输入图片验证后点击登录,会存放在request请求中, 后端需要从request请求中读取到图片验证,判断前后端验证码是否相同, 如果图片验证码相同之后才开始从数据库拿用户信息。否则直接抛出认证异常"

image-20221230160514310

简单点: 数据库获取用户账户之前, 先进行图片验证码验证

方案

怎么将字符串变成图片验证码?

这轮子肯定不能自己造, 有就拿来吧你

  • kaptcha
  • hutool

kaptcha这么玩

<!--验证码生成器-->
<dependency>
    <groupId>com.GitHub.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
    <exclusions>
        <exclusion>
            <artifactId>javax.servlet-api</artifactId>
            <groupId>javax.servlet</groupId>
        </exclusion>
    </exclusions>
</dependency>
@Bean
public DefaultKaptcha captchaProducer() {
    Properties properties = new Properties();
    properties.put("kaptcha.border", "no");
    properties.put("kaptcha.textproducer.char.length","4");
    properties.put("kaptcha.image.height","50");
    properties.put("kaptcha.image.width","150");
    properties.put("kaptcha.obscurificator.impl","com.Google.code.kaptcha.impl.ShadowGimpy");
    properties.put("kaptcha.textproducer.font.color","black");
    properties.put("kaptcha.textproducer.font.size","40");
    properties.put("kaptcha.noise.impl","com.google.code.kaptcha.impl.NoNoise");
    //properties.put("kaptcha.noise.impl","com.google.code.kaptcha.impl.DefaultNoise");
    properties.put("kaptcha.textproducer.char.string","acdefhkmnprtwxy2345678");
    DefaultKaptcha kaptcha = new DefaultKaptcha();
    kaptcha.setConfig(new Config(properties));
    return kaptcha;
}
@Resource
private DefaultKaptcha producer;
@GetMapping("/verify-code")
public void getVerifyCode(httpservletResponse response, HttpSession session) throws Exception {
    response.setContentType("image/jpeg");
    String text = producer.createText();
    session.setAttribute("verify_code", text);
    BufferedImage image = producer.createImage(text);
    try (ServletOutputStream outputStream = response.getOutputStream()) {
        ImageIO.write(image, "jpeg", outputStream);
    }
}

hutool这么玩

@GetMapping("hutool-verify-code")
public void getHtoolVerifyCode(HttpServletResponse response, HttpSession session) throws IOException {
    CircleCaptcha circleCaptcha = CaptchaUtil.createCircleCaptcha(200, 100, 4, 80);
    session.setAttribute("hutool_verify_code", circleCaptcha.getCode());
    response.setContentType(MediaType.IMAGE_PNG_VALUE);
    circleCaptcha.write(response.getOutputStream());
}

这俩随便挑选一个完事

前端就非常简单了

<fORM th:action="@{/login}" method="post">
    <div class="input">
        <label for="name">用户名</label>
        <input type="text" name="username" id="name">
        <span class="spin"></span>
    </div>
    <div class="input">
        <label for="pass">密码</label>
        <input type="passWord" name="password" id="pass">
        <span class="spin"></span>
    </div>
    <div class="input">
        <label for="code">验证码</label>
        <input type="text" name="code" id="code"><img src="/verify-code" alt="验证码">
        <!--<input type="text" name="code" id="code"><img src="/hutool-verify-code" alt="验证码">-->
        <span class="spin"></span>
    </div>
    <div class="button login">
        <button type="submit">
            <span>登录</span>
            <i class="fa fa-check"></i>
        </button>
    </div>
    <div th:text="${session.SPRING_SECURITY_LAST_EXCEPTION}"></div>
</form>

传统web项目

我们现在根据上面的思路来设计设计该怎么实现这项功能

过滤器方式


@RequiredArgsConstructor
public class ValidateCodeFilter extends OncePerRequestFilter {
    private final String login;
    private static final AntPathRequestMatcher requiresAuthenticationRequestMatcher = new AntPathRequestMatcher(this.login,
            "POST");
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (requiresAuthenticationRequestMatcher.matches(request)) {
            validateCode(request);
        }
        filterChain.doFilter(request, response);
    }
    private void validateCode(HttpServletRequest request) {
        HttpSession session = request.getSession();
        // 获取保存在session中的code
        String verifyCode = (String) session.getAttribute("verify_code");
        if (StringUtils.isBlank(verifyCode)) {
            throw new ValidateCodeException("请重新申请验证码!");
        }
        // 拿到前端的 code
        String code = request.getParameter("code");
        if (StringUtils.isBlank(code)) {
            throw new ValidateCodeException("验证码不能为空!");
        }
        // 对比
        if (!StringUtils.equalsIgnoreCase(code, verifyCode)) {
            throw new AuthenticationServiceException("验证码错误!");
        }
        // 删除掉 session 中的 verify_code
        session.removeAttribute("verify_code");
    }
}

虽然OncePerRequestFilter每次浏览器请求过来, 都会调用过滤器. 但是过滤器顺序是非常重要的

@Controller
@Slf4j
public class IndexController {
   @GetMapping("login")
   public String login() {
      return "login";
   }
   @GetMapping("")
   @ResponseBody
   public Principal index(Principal principal) {
      return principal;
   }
}
@Configuration
public class SecurityConfig {
	public static final String[] MATCHERS_URLS = {"/verify-code",
			"/CSS
@Bean
public MyDaoAuthenticationProvider authenticationProvider() throws Exception {
    MyDaoAuthenticationProvider authenticationProvider = new MyDaoAuthenticationProvider(Constants.LOGIN_USERNAME, Constants.LOGIN_PASSWORD);
    authenticationProvider.setPasswordEncoder(passwordEncoder());
    authenticationProvider.setUserDetailsService(inMemoryUserDetailsManager());
    return authenticationProvider;
}
// 往子类AuthenticationManager里面添加的 authenticationProvider
httpSecurity.authenticationProvider(authenticationProvider());

小黑: "这上面的代码有问题, AuthenticationManger有父类和子类, 上面这段代码同时往父类和子类都添加MyDaoAuthenticationProvider, 这样MyDaoAuthenticationProvider会被执行两次, 但request的流只能执行一次, 会报错"

小黑: "我们可以这么玩"

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    // 代码省略
    // 代码省略
    // 代码省略
    // 代码省略
    // 往子类AuthenticationManager里面添加的 authenticationProvider, 但不能阻止 AuthenticationManger 父类加载 DaoAuthenticationProvider
    AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
    // 但是这种方式可以将 parent Manager 设置为 null, 所以是可以的
    authenticationManagerBuilder.parentAuthenticationManager(null);
    MyDaoAuthenticationProvider authenticationProvider = new MyDaoAuthenticationProvider(Constants.LOGIN_USERNAME, Constants.LOGIN_PASSWORD);
    authenticationProvider.setPasswordEncoder(passwordEncoder());
    authenticationProvider.setUserDetailsService(inMemoryUserDetailsManager());
    authenticationManagerBuilder.authenticationProvider(authenticationProvider);
    http.authenticationManager(authenticationManagerBuilder.build());
    return http.build();
}

小黑: "SecurityFilterChain表示一个Filter集合, 更直接点就是子类的AuthenticationManager"

小黑: "所以这种玩法是给子类AuthenticationManager添加Provider, 但是它需要手动将parent置为 null, 否则父类的DaoAuthenticationProvider还是会执行, 最后报错信息就不对了, 本来应该是验证码错误, 将会变成用户名和密码错误"

小黑: "还有就是, 很多人很喜欢在旧版本像下面这么玩"

@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
   MyDaoAuthenticationProvider authenticationProvider = new MyDaoAuthenticationProvider(Constants.LOGIN_USERNAME, Constants.LOGIN_PASSWORD);
   authenticationProvider.setPasswordEncoder(passwordEncoder());
   authenticationProvider.setUserDetailsService(inMemoryUserDetailsManager());
   return new ProviderManager(authenticationProvider);
}

小黑: "在新版本也类似的这么搞, 但这样是有区别的, 下面这种方式只会加入到spring Bean上下文, 但是不会加入到Spring Security中执行, 他是无效的"

@Bean
public ProviderManager providerManager() throws Exception {
   MyDaoAuthenticationProvider authenticationProvider = authenticationProvider();
   return new ProviderManager(authenticationProvider);
}

小黑: "在新版本中, 使用上面那段代码是一点用都没有"

public MyDaoAuthenticationProvider authenticationProvider() throws Exception {
   MyDaoAuthenticationProvider authenticationProvider = new MyDaoAuthenticationProvider(Constants.LOGIN_USERNAME, Constants.LOGIN_PASSWORD);
   authenticationProvider.setPasswordEncoder(passwordEncoder());
   authenticationProvider.setUserDetailsService(inMemoryUserDetailsManager());
   return authenticationProvider;
}
// 往子类AuthenticationManager里面添加的 authenticationProvider
httpSecurity.authenticationProvider(authenticationProvider());

小黑: "上面这样做也是不行, 他还是会存在两个, 一个是MyDaoAuthenticationProvider(子类), 另一个是DaoAuthenticationProvider(父类)"

小白: "那最好的办法是什么?"

小黑: "直接将MyDaoAuthenticationProvider添加到Spring Bean上下文"

@Bean
public MyDaoAuthenticationProvider authenticationProvider() throws Exception {
    MyDaoAuthenticationProvider authenticationProvider = new MyDaoAuthenticationProvider(Constants.LOGIN_USERNAME, Constants.LOGIN_PASSWORD);
    authenticationProvider.setPasswordEncoder(passwordEncoder());
    authenticationProvider.setUserDetailsService(inMemoryUserDetailsManager());
    return authenticationProvider;
}

小白: "那还有别的思路么?"

小黑: "还有么? 不清楚了, 万能网友应该知道"

小白: "就这样设置就行了? 其他还需不需要配置?"

小黑: "其他和过滤器方式一致"

总结下

@Bean
public MyDaoAuthenticationProvider authenticationProvider() throws Exception {
// 最好的办法就是直接MyDaoAuthenticationProvider加入到Spring Bean里面就行了, 其他都不要
MyDaoAuthenticationProvider authenticationProvider = new MyDaoAuthenticationProvider(Constants.LOGIN_USERNAME, Constants.LOGIN_PASSWORD);
authenticationProvider.setPasswordEncoder(passwordEncoder());
authenticationProvider.setUserDetailsService(inMemoryUserDetailsManager());
return authenticationProvider;
}

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
 // 代码省略
 // 代码省略
 // 代码省略
 // 代码省略
 // 往子类AuthenticationManager里面添加的 authenticationProvider, 但不能阻止 AuthenticationManger 父类加载 DaoAuthenticationProvider
 AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
 // 但是这种方式可以将 parent Manager 设置为 null, 所以是可以的
 authenticationManagerBuilder.parentAuthenticationManager(null);
 MyDaoAuthenticationProvider authenticationProvider = new MyDaoAuthenticationProvider(Constants.LOGIN_USERNAME, Constants.LOGIN_PASSWORD);
 authenticationProvider.setPasswordEncoder(passwordEncoder());
 authenticationProvider.setUserDetailsService(inMemoryUserDetailsManager());
 authenticationManagerBuilder.authenticationProvider(authenticationProvider);
 http.authenticationManager(authenticationManagerBuilder.build());
 return http.build();
}

都是可以的, 一个往父类的AuthenticationManager添加MyDaoAuthenticationProvider, 另一个往子类添加, 设置父类为null

前后端分离项目

小白: "前后端分离和传统web项目的区别是什么?"

小黑: "请求request和响应response都使用JSON传递数据"

小白: "那我们分析源码时只要关注 requestresponse 咯, 只要发现存在request的读, 和 response的写通通都要重写一边"

小黑: "是的, 其实很简单, 无非是图片验证码改用json读, 认证时的读取usernamepassword也使用json读, 其次是出现异常需要响应response, 也改成json写, 认证成功和失败需要响应到前端也改成json写"

小白: "哦, 那只要分析过源码, 就能够完成前后端分离功能了"

小黑: "所以还讲源码么? "

小白: "不用, 非常简单"

基于过滤器方式

public class VerifyCodeFilter extends UsernamePasswordAuthenticationFilter {
   @Resource
   private ObjectMapper objectMapper;
   
   @SneakyThrows
   @Override
   public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
      if (!"POST".equals(request.getMethod())) {
         throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
      }
      String contentType = request.getContentType();
      HttpSession session = request.getSession();
      if (MediaType.APPLICATION_JSON_VALUE.equals(contentType) || MediaType.APPLICATION_JSON_UTF8_VALUE.equals(contentType)) {
         Map map = objectMapper.readValue(request.getInputStream(), Map.class);
         imageJSONVerifyCode(session, map);
         String username = (String) map.get(this.getUsernameParameter());
         username = (username != null) ? username.trim() : "";
         String password = (String) map.get(this.getPasswordParameter());
         password = (password != null) ? password : "";
         UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
               password);
         // Allow subclasses to set the "details" property
         setDetails(request, authRequest);
         return this.getAuthenticationManager().authenticate(authRequest);
      }
      imageVerifyCode(request, session);
      return super.attemptAuthentication(request, response);
   }
   private void imageJSONVerifyCode(HttpSession session, Map map) throws ValidateCodeException {
      String verifyCode = (String) map.get(Constants.VERIFY_CODE);
      String code = (String) session.getAttribute(Constants.VERIFY_CODE);
      if (StrUtil.isBlank(verifyCode) || StrUtil.isBlank(code) || !StrUtil.equalsIgnoreCase(verifyCode, code)) {
         throw new ValidateCodeException("验证码错误, 请重新获取验证码");
      }
   }
   private void imageVerifyCode(HttpServletRequest request, HttpSession session) throws ValidateCodeException {
      String verifyCode = request.getParameter(Constants.VERIFY_CODE);
      String code = (String) session.getAttribute(Constants.VERIFY_CODE);
      if (StrUtil.isBlank(verifyCode) || StrUtil.isBlank(code) || !StrUtil.equalsIgnoreCase(verifyCode, code)) {
         throw new ValidateCodeException("验证码错误, 请重新获取验证码");
      }
   }
}

小白: "为什么你要写imageJSONVerifyCode, imageVerifyCode两个函数? 写一个不就行了?"

小黑: "额, 是的, 把参数改成两个String verifyCode, String code也行"

@Configuration
public class SecurityConfig {
   @Resource
   private AuthenticationConfiguration authenticationConfiguration;
   @Bean
   PasswordEncoder passwordEncoder() {
      return PasswordEncoderFactories.createDelegatingPasswordEncoder();
   }
   @Bean
   public ObjectMapper objectMapper() throws Exception {
      return new ObjectMapper();
   }
   @Bean
   public VerifyCodeFilter verifyCodeFilter() throws Exception {
      VerifyCodeFilter verifyCodeFilter = new VerifyCodeFilter();
      verifyCodeFilter.setAuthenticationManager(authenticationConfiguration.getAuthenticationManager());
      verifyCodeFilter.setAuthenticationFailureHandler((request, response, exception) -> {
         HashMap<String, Object> map = new HashMap<>();
         map.put("status", 401);
         map.put("msg", exception.getMessage());
         response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
         response.getWriter().write(JSONUtil.toJsonStr(map));
      });
      verifyCodeFilter.setAuthenticationSuccesshandler((request, response, authentication) -> {
         HashMap<String, Object> map = new HashMap<>();
         map.put("status", 200);
         map.put("msg", "登录成功");
         map.put("user", authentication);
         response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
         response.getWriter().write(JSONUtil.toJsonStr(map));
      });
      return verifyCodeFilter;
   }
   @Bean
   SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
      httpSecurity
            .authorizeHttpRequests()
            .antMatchers(Constants.MATCHERS_LIST)
            .permitAll()
            .anyRequest()
            .authenticated()
      ;
      httpSecurity.formLogin()
            .loginPage(Constants.LOGIN_PAGE)
            .loginProcessingUrl(Constants.LOGIN_PROCESSING_URL)
            .defaultSuccessUrl(Constants.SUCCESS_URL, true)
            .permitAll();
      httpSecurity.logout()
            .clearAuthentication(true)
            .invalidateHttpSession(true)
            .logoutSuccessHandler((request, response, authentication) -> {
               HashMap<String, Object> map = new HashMap<>();
               map.put("status", 200);
               map.put("msg", "注销成功");
               map.put("user", authentication);
               response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
               response.getWriter().write(JSONUtil.toJsonStr(map));
            });
      httpSecurity.csrf()
            .disable();
      httpSecurity.addFilterAt(verifyCodeFilter(), UsernamePasswordAuthenticationFilter.class);
      httpSecurity.exceptionHandling()
            .accessDeniedHandler((request, response, accessDeniedException) -> {
               HashMap<String, Object> map = new HashMap<>();
               map.put("status", 401);
               map.put("msg", "您没有权限, 拒绝访问: " + accessDeniedException.getMessage());
//             map.put("msg", "您没有权限, 拒绝访问");
               response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
               response.getWriter().write(JSONUtil.toJsonStr(map));
            })
            .authenticationEntryPoint((request, response, authException) -> {
               HashMap<String, Object> map = new HashMap<>();
               map.put("status", HttpStatus.UNAUTHORIZED.value());
               map.put("msg", "认证失败, 请重新认证: " + authException.getMessage());
//             map.put("msg", "认证失败, 请重新认证");
               response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
               response.getWriter().write(JSONUtil.toJsonStr(map));
            });
      return httpSecurity.build();
   }
}

image-20221231203654101

image-20221231203711668

注意这两行代码, 教你怎么在不使用WebSecurityConfigurerAdapter的情况下拿到AuthenticationManager

@RestController
@Slf4j
public class VerifyCodeController {
   @GetMapping("/verify-code")
   public void getVerifyCode(HttpServletResponse response, HttpSession session) throws Exception {
      GifCaptcha captcha = CaptchaUtil.createGifCaptcha(Constants.IMAGE_WIDTH, Constants.IMAGE_HEIGHT);
      RandomGenerator randomGenerator = new RandomGenerator(Constants.BASE_STR, Constants.RANDOM_LENGTH);
      captcha.setGenerator(randomGenerator);
      captcha.createCode();
      String code = captcha.getCode();
      session.setAttribute(Constants.VERIFY_CODE, code);
      ServletOutputStream outputStream = response.getOutputStream();
      captcha.write(outputStream);
      outputStream.flush();
      outputStream.close();
   }
}
@Controller
@Slf4j
public class IndexController {
   @GetMapping("login")
   public String login() {
      return "login";
   }
   @GetMapping("")
   @ResponseBody
   public Principal myIndex(Principal principal) {
      return principal;
   }
}

基于认证器方式

public class MyDaoAuthenticationProvider extends DaoAuthenticationProvider {
   @Resource
   private ObjectMapper objectMapper;
   private final String loginUsername;
   private final String loginPassword;
   public MyDaoAuthenticationProvider(String loginUsername, String loginPassword) {
      this.loginUsername = loginUsername;
      this.loginPassword = loginPassword;
   }
   @SneakyThrows
   @Override
   public Authentication authenticate(Authentication authentication) throws AuthenticationException {
      ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
      assert requestAttributes != null;
      HttpServletRequest request = requestAttributes.getRequest();
      String contentType = request.getContentType();
      String verifyCode = (String) request.getSession().getAttribute(Constants.VERIFY_CODE);
      if (MediaType.APPLICATION_JSON_VALUE.equals(contentType) || MediaType.APPLICATION_JSON_UTF8_VALUE.equals(contentType)) {
         Map map = this.objectMapper.readValue(request.getInputStream(), Map.class);
         String code = (String) map.get(Constants.VERIFY_CODE);
         imageVerifyCode(verifyCode, code);
         String username = (String) map.get(loginUsername);
         String password = (String) map.get(loginPassword);
         UsernamePasswordAuthenticationToken authenticationToken = UsernamePasswordAuthenticationToken
               .unauthenticated(username, password);
         return super.authenticate(authenticationToken);
      }
      String code = request.getParameter(Constants.VERIFY_CODE);
      imageVerifyCode(verifyCode, code);
      return super.authenticate(authentication);
   }
   private void imageVerifyCode(String verifyCode, String code) throws ValidateCodeException {
      if (StrUtil.isBlank(verifyCode) || StrUtil.isBlank(code) || !StrUtil.equalsIgnoreCase(verifyCode, code)) {
         throw new ValidateCodeException("验证码错误, 请重新获取验证码");
      }
   }
}
@Slf4j
@Configuration
public class SecurityConfig {
   private static final String NOOP_PASSWORD_PREFIX = "{noop}";
   private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile("^\\{.+}.*$");
   @Resource
   private SecurityProperties properties;
   @Bean
   PasswordEncoder passwordEncoder() {
      return PasswordEncoderFactories.createDelegatingPasswordEncoder();
   }
   @Bean
   public ObjectMapper objectMapper() {
      return new ObjectMapper();
   }
   @Bean
   @Lazy
   public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
      SecurityProperties.User user = properties.getUser();
      List<String> roles = user.getRoles();
      return new InMemoryUserDetailsManager(
            User.withUsername(user.getName()).password(getOrDeducePassword(user, passwordEncoder()))
                  .roles(StringUtils.toStringArray(roles)).build());
   }
   private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) {
      String password = user.getPassword();
      if (user.isPasswordGenerated()) {
         log.warn(String.format(
               "%n%nUsing generated security password: %s%n%nThis generated password is for development use only. "
                     + "Your security configuration must be updated before running your application in "
                     + "production.%n",
               user.getPassword()));
      }
      if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) {
         return password;
      }
      return NOOP_PASSWORD_PREFIX + password;
   }
   @Bean
   public MyDaoAuthenticationProvider authenticationProvider() throws Exception {
      MyDaoAuthenticationProvider authenticationProvider = new MyDaoAuthenticationProvider(Constants.LOGIN_USERNAME, Constants.LOGIN_PASSWORD);
      authenticationProvider.setPasswordEncoder(passwordEncoder());
      authenticationProvider.setUserDetailsService(inMemoryUserDetailsManager());
      return authenticationProvider;
   }
   @Bean
   SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
      http
            .authorizeHttpRequests()
            .antMatchers(Constants.MATCHERS_LIST)
            .permitAll()
            .anyRequest()
            .authenticated()
      ;
      http.formLogin()
            .loginPage(Constants.LOGIN_PAGE)
            .loginProcessingUrl(Constants.LOGIN_PROCESSING_URL)
            .successHandler(new MyAuthenticationSuccessHandler())
            .failureHandler(new MyAuthenticationFailureHandler())
            .permitAll();
      http.logout()
            .clearAuthentication(true)
            .invalidateHttpSession(true)
            .logoutSuccessHandler(new MyLogoutSuccessHandler());
      http.csrf()
            .disable();
      http.exceptionHandling(exceptionHandlinGConfigurer -> {
         exceptionHandlingConfigurer.authenticationEntryPoint(new MyAuthenticationEntryPoint());
         exceptionHandlingConfigurer.accessDeniedHandler(new MyAccessDeniedHandler());
      })
      ;
      return http.build();
   }
   private static class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
      @Override
      public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
         HashMap<String, Object> map = new HashMap<>();
         map.put("status", 200);
         map.put("msg", "认证成功");
         map.put("user_info", authentication.getPrincipal());
         response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
         response.getWriter().write(JSONUtil.toJsonStr(map));
      }
   }
   private static class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
      @Override
      public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
         log.error("认证失败", exception);
         exception.printStackTrace();
         HashMap<String, Object> map = new HashMap<>();
         map.put("status", 401);
         map.put("msg", "认证失败");
         map.put("exception", exception.getMessage());
         response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
         response.getWriter().write(JSONUtil.toJsonStr(map));
      }
   }
   private static class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
      @Override
      public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
         log.error("认证失效", authException);
         HashMap<String, Object> map = new HashMap<>();
         map.put("status", HttpStatus.UNAUTHORIZED.value());
         map.put("msg", "认证失败, 请重新认证: " + authException.getMessage());
//             map.put("msg", "认证失败, 请重新认证");
         response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
         response.getWriter().write(JSONUtil.toJsonStr(map));
      }
   }
   private static class MyAccessDeniedHandler implements AccessDeniedHandler {
      @Override
      public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
         log.error("没有权限", accessDeniedException);
         HashMap<String, Object> map = new HashMap<>();
         map.put("status", 401);
         map.put("msg", "您没有权限, 拒绝访问: " + accessDeniedException.getMessage());
//             map.put("msg", "您没有权限, 拒绝访问");
         response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
         response.getWriter().write(JSONUtil.toJsonStr(map));
      }
   }
   private static class MyLogoutSuccessHandler implements LogoutSuccessHandler {
      @Override
      public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
         HashMap<String, Object> map = new HashMap<>();
         map.put("status", 200);
         map.put("msg", "注销成功");
         map.put("user", authentication);
         response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
         response.getWriter().write(JSONUtil.toJsonStr(map));
      }
   }
}

以上就是Spring Security实现添加图片验证功能的详细内容,更多关于Spring Security添加图片验证的资料请关注编程网其它相关文章!

--结束END--

本文标题: SpringSecurity实现添加图片验证功能

本文链接: https://lsjlt.com/news/176716.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • SpringSecurity实现添加图片验证功能
    目录本章内容思路方案怎么将字符串变成图片验证码kaptcha这么玩hutool这么玩传统web项目过滤器方式认证器方式总结下前后端分离项目基于过滤器方式基于认证器方式本章内容 Spr...
    99+
    2023-01-04
    Spring Security添加图片验证 Spring Security图片验证
  • JS实现图片验证码功能
    本文实例为大家分享了JS实现图片验证码功能的具体代码,供大家参考,具体内容如下 以下代码可以直接copy运行,不需要引入jquery.jar 1. html代码 <%@ pag...
    99+
    2024-04-02
  • Java实现图片验证码功能
    简介 在实现登录功能时,一般为了安全都会设置验证码登录,为了防止某个用户用特定的程序暴力破解方式进行不断的尝试登录。常见验证码分为图片验证码和短信验证码,还有滑动窗口模块和选中指定物...
    99+
    2024-04-02
  • vue 实现左滑图片验证功能
    目录前言一、安装二、使用前言 众所周知,网页中滑动图片验证一直是各大网站、移动端的主流校验方式,其主要作用是为了区分人和机器以及为了防止机器人程序暴力登录或攻击从而设置的一种安全保护...
    99+
    2023-05-14
    vue 左滑图片验证 vue 图片验证 vue 左滑验证
  • vue实现图片滑动验证功能
    图片滑动验证,是目前比较常见的验证方式,主要目的是防止用户利用机器人自动注册、登录、灌水。 目前vue技术日趋成熟,已经有专门针对图片滑动验证功能的插件了。具体使用方式如下: 1....
    99+
    2024-04-02
  • Springboot+SpringSecurity怎么实现图片验证码登录
    本文小编为大家详细介绍“Springboot+SpringSecurity怎么实现图片验证码登录”,内容详细,步骤清晰,细节处理妥当,希望这篇“Springboot+SpringSecurity怎么实现图片验证码登录”文章能帮助大家解决疑惑...
    99+
    2023-06-30
  • jsp实现简单图片验证码功能
    本文实例为大家分享了jsp实现简单图片验证码的具体代码,供大家参考,具体内容如下 一、实现的功能分析 (1)在登陆页面加验证码的功能,起到一定的安全性。在输入正确的验证码,用户名和密...
    99+
    2024-04-02
  • JS如何实现图片验证码功能
    本篇内容主要讲解“JS如何实现图片验证码功能”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JS如何实现图片验证码功能”吧!1. html代码<%@ page langu...
    99+
    2023-06-30
  • Springboot+SpringSecurity实现图片验证码登录的示例
    这个问题,网上找了好多,结果代码都不全,找了好多,要不是就自动注入的类注入不了,编译报错,要不异常捕获不了浪费好多时间,就觉得,框架不熟就不能随便用,全是坑,气死我了,最后改了两天....
    99+
    2024-04-02
  • SpringBoot整合kaptcha实现图片验证码功能
    目录栗子配置文件SpringBoot项目中pom.xml文件项目代码项目结构SpringBootVerifyCodeApplication.javaVerifyCodeConfig....
    99+
    2024-04-02
  • vue如何实现左滑图片验证功能
    这篇“vue如何实现左滑图片验证功能”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“vue如何实现左滑图片验证功能”文章吧。一...
    99+
    2023-07-05
  • 怎么用php实现添加图片功能
    使用PHP语言实现添加图片功能比较简单,下面将介绍一种基于Web的PHP添加图片功能的实现方法。步骤一:设置表单在一个HTML文件中,创建一个form表单,设置enctype为“multipart/form-data”。这个用于告诉服务器,...
    99+
    2023-05-14
    php
  • 如何用php实现添加图片功能
    这篇文章主要介绍“如何用php实现添加图片功能”,在日常操作中,相信很多人在如何用php实现添加图片功能问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何用php实现添加图片功能”的疑惑有所帮助!接下来,请跟...
    99+
    2023-07-05
  • vue动态添加表单validateField验证功能实现
    vue动态添加表单validateField验证,代码如下所示: <template> <el-form ref="form" :model="form" ...
    99+
    2023-05-14
    vue动态添加表单 vue  validateField验证
  • springboot图片验证码功能模块怎么实现
    本篇内容主要讲解“springboot图片验证码功能模块怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“springboot图片验证码功能模块怎么实现”吧!具体效果如下:工具类该工具类为生...
    99+
    2023-06-30
  • SpringBoot+SpringSecurity+jwt实现验证
    目录环境目录结构信息记录一下使用springSecurity实现jwt的授权方法,这方法可以实现权限的基本认证。当然这个案例还有许多的问题,不过还是先记录一下。其他功能以后在补充。 ...
    99+
    2024-04-02
  • 怎么用Springboot +redis+Kaptcha实现图片验证码功能
    这篇文章主要介绍了怎么用Springboot +redis+Kaptcha实现图片验证码功能的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么用Springboot +redis+Kaptc...
    99+
    2023-06-29
  • android实现一个图片验证码倒计时功能
    1.如图所示,要实现一个验证码的倒计时的效果                &n...
    99+
    2023-05-30
    android 验证码 倒计时
  • 基于Hutool的图片验证码功能模块实现
    目录简介Hutool名称的由来基于Hutool的图片验证码功能模块实现1.背景2.方案设计2.1 方案步骤2.2 Hutool工具类引入2.5 方案交互图3.模块编写4.接口测试简介...
    99+
    2022-11-13
    Hutool图片验证码 Hutool图片验证码
  • vue动态添加表单validateField验证功能如何实现
    今天小编给大家分享一下vue动态添加表单validateField验证功能如何实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作