springsecurity6使用

spring security 中的类 :

  • AuthenticationManager : 实现类:ProviderManager
    管理很多的 provider ,,, 经常使用的,DaoAuthenticationProvider , 这个要设置一个 UserDetailService , 查找数据库,,loadUserByUsername() 查找出数据库中的对象,,然后进行比对
spring security 中的配置

配置spring security 也就是配置 过滤器链,,spring security 他有默认的过滤器链,,,通过HttpSecurity 中的 build()方法,会返回一个默认的有拦截的过滤器链
我们一般都是在这个原本的过滤器链上面修改,,而不是重新创建自己的过滤器链,,


    /**
     * 过滤器
     *  : 配置过滤器链
     *  DispatchServlet
     *
     *  DefaultLoginPageGeneratingFilter : 默认登录页面过滤器
     *  DefaultLogoutPageGeneratingFilter : 默认注销页面过滤器
     *  BasicAuthenticationFilter : 请求头认证过滤器
     */


    /**
     * 配置过滤器链  SecurityFilterChain,,spring security 所有功能都是通过过滤器链来提供
     */
    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // 拦截所有,,经过某些过滤器
//        return new DefaultSecurityFilterChain(new AntPathRequestMatcher("/**"));


        // 默认的过滤器链
//        return http.build();


         http.authorizeHttpRequests(p->p.anyRequest().authenticated())
                .formLogin(f->f.usernameParameter("username")
                        .passwordParameter("password")
                        .loginProcessingUrl("/login")
                        .successHandler((req,resp,auth)->{
                            resp.setContentType("application/json;charset=utf-8");
                            Hr hr = (Hr) auth.getPrincipal();
                            hr.setPassword(null);
                            resp.getWriter().write(new ObjectMapper().writeValueAsString(RespBean.ok("登录成功",hr)));
                        })
                        .failureHandler((req,resp,e)->{
                            resp.setContentType("application/json;charset=utf-8");
                            RespBean error = RespBean.error("登录失败");

                            if (e instanceof BadCredentialsException){
                                error.setMessage("密码错误");
                            }else if (e instanceof DisabledException){
                                error.setMessage("用户被禁用");
                            }else if (e instanceof LockedException){
                                error.setMessage("账户被锁定");
                            }else if (e instanceof AccountExpiredException){
                                error.setMessage("账户过期");
                            }else if(e instanceof CredentialsExpiredException){
                                error.setMessage("密码过期");
                            }

                            resp.getWriter().write(new ObjectMapper().writeValueAsString(error));
                        }))
                .csrf(c->c.disable())
                 //异常处理
                 .exceptionHandling(e->e.authenticationEntryPoint((req,resp,ex)->{
                     resp.setContentType("application/json;charset=utf-8");
                     resp.setStatus(401);
                     RespBean error = RespBean.error("尚未登陆,请登录");
                     resp.getWriter().write(new ObjectMapper().writeValueAsString(error));
                 }));

         // 加到 UsernamePasswordAuthenticationFilter前面
         http.addFilterBefore(jsonFilter(), UsernamePasswordAuthenticationFilter.class);

         return  http.build();

        /**
         * spring security 默认key-value
         * UsernamePasswordAuthenticationFilter
         */

    }

UsernamePasswordAuthenticationFilter : 这个是拦截提交的用户名密码的拦截器,,,里面有个attemptAuthentication() 去获取前端传入的用户名密码,
springsecurity6使用_第1张图片
根据request获取的参数,,,
然而,我们需要通过json传参,,就需要重写这个方法,,并将自己的过滤器加入到spring security的过滤器链中,,

/**
 * 登录传递json
 */
public class JsonFilter extends UsernamePasswordAuthenticationFilter {
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }

        String contentType = request.getContentType();
        if (contentType.equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE) || contentType.equalsIgnoreCase(MediaType.APPLICATION_JSON_UTF8_VALUE)){
            // 前端传入的是json
            try {
                // 通过io流,去解析请求体参数,,,比如:文件,json,,,   key-value也可以通过io流获取
                Hr hr = new ObjectMapper().readValue(request.getInputStream(), Hr.class);

                String username = hr.getUsername();
                String password = hr.getPassword();
                UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
                        password);
                // Allow subclasses to set the "details" property
                setDetails(request, authRequest);
                // 获取认证管理器去认证
                return this.getAuthenticationManager().authenticate(authRequest);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }else{
            //  key-value
            return super.attemptAuthentication(request,response);
        }




    }
}

自己新加的过滤器,需要配置自己的 AuthenticationManager , 和用户信息存放的位置:

   /**
     * AuthenticationManager :
     *      实现类:   ProviderManager
     *      管理很多 provider
     * @return
     */
    @Bean
    AuthenticationManager authenticationManager(){
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();

        daoAuthenticationProvider.setUserDetailsService(hrService);

        ProviderManager providerManager = new ProviderManager(daoAuthenticationProvider);
        return providerManager;
    }

配置了自己的 登录filter,, HttpSecurity中配置的formLogin 就失效了,,,需要自己配置loginProcessingUrl, successHandler,failureHandler等信息,,

需要配置自己的 AuthenticationManager 和表明登录信息的存放位子,,,,因为每一次都会从这个存放位置去找用户信息,,如果找到,表示已登录,如果没找到,就是没有登录

   /**
     * 配置了  JsonFilter ,,,    httpsecurity 中的 fromLogin就失效了
     * @return
     */
    JsonFilter jsonFilter(){
        JsonFilter jsonFilter = new JsonFilter();
        jsonFilter.setFilterProcessesUrl("/login");
        jsonFilter.setAuthenticationSuccessHandler((req,resp,auth)->{
            resp.setContentType("application/json;charset=utf-8");
            Hr hr = (Hr) auth.getPrincipal();
            hr.setPassword(null);
            resp.getWriter().write(new ObjectMapper().writeValueAsString(RespBean.ok("登录成功",hr)));
        });
        jsonFilter.setAuthenticationFailureHandler((req,resp,e)->{
            resp.setContentType("application/json;charset=utf-8");
            RespBean error = RespBean.error("登录失败");

            if (e instanceof BadCredentialsException){
                error.setMessage("密码错误");
            }else if (e instanceof DisabledException){
                error.setMessage("用户被禁用");
            }else if (e instanceof LockedException){
                error.setMessage("账户被锁定");
            }else if (e instanceof AccountExpiredException){
                error.setMessage("账户过期");
            }else if(e instanceof CredentialsExpiredException){
                error.setMessage("密码过期");
            }

            resp.getWriter().write(new ObjectMapper().writeValueAsString(error));
        });


        // 需要设置自己的 AuthenticationManager
        jsonFilter.setAuthenticationManager(authenticationManager());

        /**
         *  每一次都会从 httpSession中获取用户,,如果httpsession中没有用户,就会表示成没有登录,,
         *  新配置的 filter 需要告知 ,,用户信息存放在哪里,,
         */

        // 自己配置的filter 需要设置 SecurityContextHolder 存储用户的位置
        jsonFilter.setSecurityContextRepository(new HttpSessionSecurityContextRepository());

        return jsonFilter;
    }

这个用户信息可以存在HttpSessionSecurityContextRepositorysession中,,也可以重写类,存放在其他地方,比如redis

spring security 异常处理,,exceptionHandling, 中authenticationEntryPoint,处理登录失败异常
springsecurity6使用_第2张图片

你可能感兴趣的:(spring,security)