zoukankan      html  css  js  c++  java
  • spring-security使用-更友好的方式扩展登录AuthenticationProvider(三)

    说明

    在 spring-security使用-登录(一) 我们使用的是重写了Spring-security的filter的方式来进行自定义,但是这样的弊端,就是侵入太大。直接把spring-security的filter给替换掉了,

    通过AuthenticationProvider的方式是在spring-security的filter内部留的扩展点进行扩展自定义登录逻辑

    接口定义

    AuthenticationProvider

    public interface AuthenticationProvider {
        /**
         * 验证用户身份
         * @param var1
         * @return
         * @throws AuthenticationException
         */
        Authentication authenticate(Authentication var1) throws AuthenticationException;
    
        /**
         * supports 则用来判断当前的 AuthenticationProvider 是否支持对应的 Authentication。
         * @param var1
         * @return
         */
        boolean supports(Class<?> var1);
    }

    Authentication

    封装用户身份信息

    public interface Authentication extends Principal, Serializable {
        //用来获取用户的权限。
        Collection<? extends GrantedAuthority> getAuthorities();
        //方法用来获取用户凭证,一般来说就是密码。
        Object getCredentials();
        //方法用来获取用户携带的详细信息,可能是当前请求之类的东西。
        Object getDetails();
        //方法用来获取当前用户,可能是一个用户名,也可能是一个用户对象。
        Object getPrincipal();
        //当前用户是否认证成功。
        boolean isAuthenticated();
    
        void setAuthenticated(boolean var1) throws IllegalArgumentException;
    }

    类图

     

    UsernamePasswordAuthenticationFilter 用的就是UserNamePasswordAuthenticationToken

    使用AuthenticationProvider自定义登录逻辑

    1.增加自定义provider继承DaoAuthenticationProvider 

    public class CodeAuthenticationProvider extends DaoAuthenticationProvider {
        @Override
        protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication)   {
            HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String code = req.getParameter("code");
            String verify_code = (String) req.getSession().getAttribute("verify_code");
            if (code == null || verify_code == null || !code.equals(verify_code)) {
                throw new AuthenticationServiceException("验证码错误");
            }
            super.additionalAuthenticationChecks(userDetails, authentication);
        }
    }

    2.在public class SecurityConfig extends WebSecurityConfigurerAdapter 类增加以下

     /**
         * 对密码进行加密的实例
         * @return
         */
        @Bean
        PasswordEncoder passwordEncoder() {
            /**
             * 不加密所以使用NoOpPasswordEncoder
             * 更多可以参考PasswordEncoder 的默认实现官方推荐使用: BCryptPasswordEncoder,BCryptPasswordEncoder
             */
            return NoOpPasswordEncoder.getInstance();
        }
        /**
         * 自定义provider
         * @return
         */
       public CodeAuthenticationProvider codeAuthenticationProvider() {
            CodeAuthenticationProvider myAuthenticationProvider = new CodeAuthenticationProvider();
            //设置passorderEncoder
            myAuthenticationProvider.setPasswordEncoder(passwordEncoder());
            //设置UserDetailsService 可以参考第一篇登录使用例子自定义userDetailServices
            myAuthenticationProvider.setUserDetailsService(userService);
            return myAuthenticationProvider;
        }
    
        /**
         * 重写父类自定义AuthenticationManager 将provider注入进去
         * 当然我们也可以考虑不重写 在父类的manager里面注入provider
         * @return
         * @throws Exception
         */
        @Override
        protected AuthenticationManager authenticationManager() throws Exception {
            ProviderManager manager = new ProviderManager(Arrays.asList(codeAuthenticationProvider()));
            return manager;
        }

    原理 

    spring-security使用-登录(一) 我们替换了默认的UsernamePasswordAuthenticationFilter

    1.根据看这个类UsernamePasswordAuthenticationFilter我们可以看出他的父类实现了Servilet Fitler,所以spring-security是基于Filter的

    org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter#doFilter 父类实现

      public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
                     //省略部分代码......
                    //调用子类的实现 也就是 UsernamePasswordAuthenticationFilter
                    authResult = this.attemptAuthentication(request, response);
                    if (authResult == null) {
                        return;
                    }
            }
        }

    2.看子类实现

    org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#attemptAuthentication

     public org.springframework.security.core.Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
            //注意这里不支持post请求,如果我们要让支持post请求,就要重写filter吧这里去掉
            if (this.postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
            } else {
                //获得登录用户名
                String username = this.obtainUsername(request);
                //用的登录密码
                String password = this.obtainPassword(request);
                if (username == null) {
                    username = "";
                }
    
                if (password == null) {
                    password = "";
                }
    
                username = username.trim();
                //通过UsernamePasswordAuthenticationToken 封装
                UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
                this.setDetails(request, authRequest);
                //委托给AuthenticationManager 执行 我们上面重写用的是ProviderManager
                return this.getAuthenticationManager().authenticate(authRequest);
            }
        }

    3.providerManger实现

    org.springframework.security.authentication.ProviderManager#authenticate

        public org.springframework.security.core.Authentication authenticate(org.springframework.security.core.Authentication authentication) throws AuthenticationException {
            //获得Authentication的类型
            Class<? extends org.springframework.security.core.Authentication> toTest = authentication.getClass();
            AuthenticationException lastException = null;
            AuthenticationException parentException = null;
            org.springframework.security.core.Authentication result = null;
            org.springframework.security.core.Authentication parentResult = null;
            boolean debug = logger.isDebugEnabled();
            //获得所有的Providers 就是我们定义的CodeAuthenticationProvider
            Iterator var8 = this.getProviders().iterator();
            while(var8.hasNext()) {
                //迭代器迭代获取
                AuthenticationProvider provider = (AuthenticationProvider)var8.next();
                //判断是否能处理
                if (provider.supports(toTest)) {
                    if (debug) {
                        logger.debug("Authentication attempt using " + provider.getClass().getName());
                    }
    
                    try {
                        //调用provider的authenticate 执行身份认证
                        result = provider.authenticate(authentication);
                        if (result != null) {
                            this.copyDetails(authentication, result);
                            break;
                        }
                    } catch (InternalAuthenticationServiceException | AccountStatusException var13) {
                        this.prepareException(var13, authentication);
                        throw var13;
                    } catch (AuthenticationException var14) {
                        lastException = var14;
                    }
                }
            }
          //省略部分代码.......
        }
  • 相关阅读:
    二叉查找树
    huffman coding
    普通树与二叉树
    递归转循环的通法
    尾递归和JAVA
    编译器和解释器
    开天辟地-用visualstudio2010编写helloworld
    Android app targetSdk升级到27碰到的一个bug补充说明
    Android Studio修改Apk打包生成名称
    Glide3升级到Glide4碰到的问题汇总以及部分代码修改
  • 原文地址:https://www.cnblogs.com/LQBlog/p/14231091.html
Copyright © 2011-2022 走看看