精华内容
下载资源
问答
  • Token原理

    2017-11-21 16:23:38
    通过token获取用户的role,动态根据用户的role算出其相应有权限的路由,通过router.addRoutes动态挂载路由。 登录 当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个token,拿到token...

    权限验证

    通过token获取用户的role,动态根据用户的role算出其相应有权限的路由,通过router.addRoutes动态挂载路由。

    登录

    当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个token,拿到token之后(我会将这个token存贮到cookie中,保证刷新页面后能记住用户登录状态),前端会根据token再去拉取一个user_info的接口来获取用户的详细信息(如用户权限,用户名等等信息)。


    • 第一次认证:第一次登录,用户从浏览器输入用户名/密码,提交后到服务器的登录处理的Action层(Login Action);
    • Login Action调用认证服务进行用户名密码认证,如果认证通过,Login Action层调用用户信息服务获取用户信息(包括完整的用户信息及对应权限信息);
    • 返回用户信息后,Login Action从配置文件中获取Token签名生成的秘钥信息,进行Token的生成;
    • 生成Token的过程中可以调用第三方的JWT Lib生成签名后的JWT数据;
    • 完成JWT数据签名后,将其设置到COOKIE对象中,并重定向到首页,完成登录过程;
      image

    用户输入

    graph LR
    用户输入账号密码-->用户名密码验证
    用户名密码验证-->验证成功返回用户信息
    验证成功返回用户信息-->利用库中的token秘钥信息生成token
    利用库中的token秘钥信息生成token-->将token存储在cookie对象中
    展开全文
  • 应该将所有的动态参数也封装进token里去,使之变成动态token,并解析比对token里的参数和获取到的参数的一致性。防止token有效,但参数被篡改!!! (相对彻底解决token被劫持,一个比较简单的解决思路:关于测试...

    在线生成非对称加密公钥私钥对、在线生成公私钥对、RSA Key pair create、生成RSA密钥对:http://web.chacuo.net/netrsakeypair

     

    RSA加密公私钥证书的生成参见自己的git工程yusp-mark。

     

    什么是数字签名的经典讲解:http://www.youdzone.com/signature.html

    数字证书工作原理及基本流程:https://blog.csdn.net/itwxming/article/details/101026306

     

    1.无状态登录原理

    1.1 什么是无状态? 


    1.1.1有状态服务:

    即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session。

    例如登录:用户登录后,我们把登录信息保存在服务端session中,并且给用户一个cookie值,记录对应的session中。下次请求,用户通过cookieId从而查询到对应的session找到用户的信息。
    缺点:

    • 服务端保存大量数据,增加服务端压力
    • 服务端保存用户状态,无法进行水平扩展
    • 客户端请求依赖服务端,多次请求必须访问同一台服务器

    1.1.2无状态服务:


    微服务集群中的每个服务,对外提供的都是Rest风格的接口。而Rest风格的一个最重要的规范就是:服务的无状态性,即:
    1、 服务端不保存任何客户端请求者信息。
    2、客户端的每次请求必须具备自描述信息,通过这些信息识别客户端身份。

    优点:

    • 客户端请求不依赖服务端的信息,任何多次请求不需要必须访问到同一台服务
    • 服务端的集群和状态对客户端透明
    • 服务端可以任意的迁移和伸缩
    • 减小服务端存储压力

    1.1.3无状态登录的过程:

    1. 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)。
    2. 认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证。
    3. 以后每次请求,客户端都携带认证的token。
    4. 服务端对token进行解密,判断是否有效。

    2 JWT

    2.1简介https://jwt.io/introduction/

    JWT,全称是Json Web Token, 是JSON风格轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权。
    JWT官网
    GitHub上jwt的java客户端

    2.2数据格式

    JWT的token通常有三个部分:标题,有效负载和签名。
                Header:标头标识用于生成签名的算法。通常头部有两部分信息:

    header = '{"alg":"HS256","typ":"JWT"}'

                      - 声明类型type,这里是JWT(type=jwt)

                      - 加密算法,自定义(rs256/base64/hs256)

                        我们会对头部进行base64加密(可解密),得到第一部分数据

                Payload:载荷,就是有效数据,包含我们希望提出的声明,一般包含下面信息:

    payload = '{"loggedInAs":"admin","iat":1422779638}'

                    - 用户身份信息-userid,username(注意,这里因为采用base64加密,可解密,因此不要存放敏感信息)

                    - 注册声明:如token的签发时间,过期时间,签发人等

                     正如JWT规范中所建议的那样,我们包含一个时间戳,称为iat“发布时”的缩写。

                      这部分也会采用base64加密,得到第二部分数据

                Signature:通过header中设置的加密算法加密。签名,是整个数据的认证信息。一般根据前两步的数据,再加上服务的的密钥(secret)(不要泄漏,最好周期性更换),通过加密算法生成。用于验证整个数据完整和可靠性。

    签名由base64url编码头和有效负载计算,并以句点作为分隔符连接:

    key = 'secretkey'
    unsignedToken = encodeBase64(header) + '.' + encodeBase64(payload)
    signature = HMAC-SHA256(key, unsignedToken)

    最后:我们base64url编码签名,并使用句点将这三个部分连接在一起:

    token = encodeBase64(header) + '.' + encodeBase64(payload) + '.' + encodeBase64(signature)
    
    # token is now:
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI

     

    2.3JWT交互流程


    流程图:

    步骤翻译:

    • 1、用户登录
    • 2、服务的认证,通过后根据secret生成token
    • 3、将生成的token返回给浏览器
    • 4、用户每次请求携带token
    • 5、服务端利用公钥解读jwt签名,判断签名有效后,从Payload中获取用户信息
    • 6、处理请求,返回响应结果

    因为JWT签发的token中已经包含了用户的身份信息,并且每次请求都会携带,这样服务的就无需保存用户信息,甚至无需去数据库查询,完全符合了Rest的无状态规范。

    2.4.加密分类-非对称加密


    加密技术是对信息进行编码和解码的技术,编码是把原来可读信息(又称明文)译成代码形式(又称密文),其逆过程就是解码(解密),加密技术的要点是加密算法,加密算法可以分为三类:

        对称加密,如AES


          基本原理:将明文分成N个组,然后使用密钥对各个组进行加密,形成各自的密文,最后把所有的分组密文进行合并,形成最终的密文。
          优势:算法公开、计算量小、加密速度快、加密效率高
          缺陷:双方都使用同样密钥,安全性得不到保证


        非对称加密,如RSA

    加密和解密用不同的秘钥,分别是私钥和公钥,这两个密钥是成对出现的,公钥加密过的密文只有对应的私钥能解密;私钥签名过的密文可以通过对应的公钥验签。原则上私钥是不能在网络中传递的。

          基本原理:同时生成两把密钥:私钥和公钥,私钥隐秘保存,公钥可以下发给信任客户端
                私钥加密,持有私钥或公钥才可以解密
                公钥加密,持有私钥才可解密
          优点:安全,难以破解
          缺点:算法比较耗时


        不可逆加密,如MD5,SHA


          基本原理:加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的,无法根据密文推算出明文。


    RSA算法历史:

    1977年,三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字缩写:RSA。

    3 使用

    3.1使用JWT

    在pom.xml中引入JWT依赖:

    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
        </dependency>
    </dependencies>
    

    创建一个token
    首先需要通过JWT.create()创建一个 JWTCreator 实例, 使用生成器定义令牌需要的自定义声明。最后获取字符串令牌调用sign()并传递算法实例。

    RSAPublicKey publicKey = //Get the key instance
    RSAPrivateKey privateKey = //Get the key instance
    try {
        Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);
        String token = JWT.create()
            .withIssuer("username")
            .sign(algorithm);
    } catch (JWTCreationException exception){
        //Invalid Signing configuration / Couldn't convert Claims.
    }
    

    验证一个token

    对于传递过来的token需要进行解析,获取对应的的用户信息,如果解析成功,则说明是同一个用户。

        /**
         * 公钥解析token
         *
         * @param token     用户请求中的token
         * @return
         */
        private static Jws<Claims> parserToken(String token, byte[] publicKey) throws Exception {
            return Jwts.parser().setSigningKey(RsaUtils.getPublicKey(publicKey))
                    .parseClaimsJws(token);
        }
    

    后续访问网站其他功能,需要携带token进行验证,如果验证不通过则可以拒绝访问。

     

    注:纯JWT在签名时的密钥安全性有问题,所以再结合RSA的公私钥安全性来解决。

     

    如果只能用http的话,前后端交互时不能只将用户信息封装进JWT中,否则token被人截获可以冒用。应该将所有的动态参数也封装进token里去,使之变成动态token,并解析比对token里的参数和获取到的参数的一致性。防止token有效,但参数被篡改!!!或者直接用https。

    展开全文
  • Android 动态换肤框架原理

    千次阅读 2020-05-24 20:19:06
    1. Android 系统PhoneWindow 源码阅读 1.1. Activity实例化 PhoneWindow Activity: final void attach... Instrumentation instr, IBinder token, int ident, Application application, Intent intent, A...

    1.  Android 系统 PhoneWindow 源码阅读

     1.1.  Activity实例化 PhoneWindow

      Activity: 
        final void attach(Context context, ActivityThread aThread,
                Instrumentation instr, IBinder token, int ident,
                Application application, Intent intent, ActivityInfo info,
                CharSequence title, Activity parent, String id,
                NonConfigurationInstances lastNonConfigurationInstances,
                Configuration config, String referrer, IVoiceInteractor voiceInteractor,
                Window window, ActivityConfigCallback activityConfigCallback) {
            attachBaseContext(context);
    
            mFragments.attachHost(null /*parent*/);
     // Activity 中通过 PhoneWindow 加载布局,PhoneWindow AndrodStudio源码无法查看
    // 上这里去看 http://androidxref.com/
            mWindow = new PhoneWindow(this, window, activityConfigCallback);
            mWindow.setWindowControllerCallback(this);
            mWindow.setCallback(this);
            mWindow.setOnWindowDismissedCallback(this);
            mWindow.getLayoutInflater().setPrivateFactory(this);
            if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
                mWindow.setSoftInputMode(info.softInputMode);
            }
            if (info.uiOptions != 0) {
                mWindow.setUiOptions(info.uiOptions);
            }
            mUiThread = Thread.currentThread();
    
            mMainThread = aThread;
            mInstrumentation = instr;
            mToken = token;
            mIdent = ident;
            mApplication = application;
            mIntent = intent;
            mReferrer = referrer;
            mComponent = intent.getComponent();
            mActivityInfo = info;
            mTitle = title;
            mParent = parent;
            mEmbeddedID = id;
            mLastNonConfigurationInstances = lastNonConfigurationInstances;
            if (voiceInteractor != null) {
                if (lastNonConfigurationInstances != null) {
                    mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
                } else {
                    mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                            Looper.myLooper());
                }
            }
    
            mWindow.setWindowManager(
                    (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                    mToken, mComponent.flattenToString(),
                    (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
            if (mParent != null) {
                mWindow.setContainer(mParent.getWindow());
            }
            mWindowManager = mWindow.getWindowManager();
            mCurrentConfig = config;
    
            mWindow.setColorMode(info.colorMode);
        }

    1.2.  PhoneWindow 中 内部 DecorView

     private DecorView mDecor;
    	// 
     int layoutResource;
     //  做一系列判断,去加载系统的 layout资源文件 
       if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
                layoutResource = R.layout.screen_swipe_dismiss;
                setCloseOnSwipeEnabled(true);
            }
    		...... 
    		// 把系统布局加入到 DecorView 中  
    		// 系统布局 是一个 FrameLayout的 ViewGroup
    		//  id 是 android.R.id.content  叫做 mContentParent
     mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); 
          // 自己调用setContenView 布局位置,自己的布局 Activity 

    内部结构: 

    2.  分析 ImageView 拦截

    // 继承Activity,那么 返回 ImageView ,继承 AppCompatActivity 返回  AppCompatImageView

    为什么?  在 AppCompatImageView 中 ImageView被替换成 AppCompatImageView, 看源码

    2.1 .AppCompatActivity  源码   getDelegate

     @Override
        public void setContentView(@LayoutRes int layoutResID) {
            getDelegate().setContentView(layoutResID);
        }
    	    @NonNull
        public AppCompatDelegate getDelegate() {
            if (mDelegate == null) {
                mDelegate = AppCompatDelegate.create(this, this);
            }
            return mDelegate;
        }

    2.2.  AppCompatDelegate 最终实例化的是 AppCompatDelegateImplV9

    private static AppCompatDelegate create(Context context, Window window,
                AppCompatCallback callback) {
            final int sdk = Build.VERSION.SDK_INT;
            if (BuildCompat.isAtLeastN()) {
                return new AppCompatDelegateImplN(context, window, callback);
            } else if (sdk >= 23) {
                return new AppCompatDelegateImplV23(context, window, callback);
            } else if (sdk >= 14) {
                return new AppCompatDelegateImplV14(context, window, callback);
            } else if (sdk >= 11) {
                return new AppCompatDelegateImplV11(context, window, callback);
            } else {
                return new AppCompatDelegateImplV9(context, window, callback);
            }
        }

    最终看 AppCompatDelegateImplV9 的 createView

    核心 LayoutInflaterCompat.setFactory2(layoutInflater, this)   那么 this  实现 factory2接口  重写factory2接口方法

     

    public static void setFactory2(
            @NonNull LayoutInflater inflater, @NonNull LayoutInflater.Factory2 factory) {
        IMPL.setFactory2(inflater, factory);
    }
    
    public interface Factory2 extends Factory {
        /**
         * Version of {@link #onCreateView(String, Context, AttributeSet)}
         * that also supplies the parent that the view created view will be
         * placed in.
         *
         * @param parent The parent that the created view will be placed
         * in; <em>note that this may be null</em>.
         * @param name Tag name to be inflated.
         * @param context The context the view is being created in.
         * @param attrs Inflation attributes as specified in XML file.
         *
         * @return View Newly created view. Return null for the default
         *         behavior.
         */
        public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
    }

     AppCompatDelegateImplV9 的  createView方法

    @Override
        public View createView(View parent, final String name, @NonNull Context context,
                @NonNull AttributeSet attrs) {
            if (mAppCompatViewInflater == null) {
                mAppCompatViewInflater = new AppCompatViewInflater();
            }
    
            boolean inheritContext = false;
            if (IS_PRE_LOLLIPOP) {
                inheritContext = (attrs instanceof XmlPullParser)
                        // If we have a XmlPullParser, we can detect where we are in the layout
                        ? ((XmlPullParser) attrs).getDepth() > 1
                        // Otherwise we have to use the old heuristic
                        : shouldInheritContext((ViewParent) parent);
            }
    
            return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
                    IS_PRE_LOLLIPOP, /* Only read android:theme pre-L (L+ handles this anyway) */
                    true, /* Read read app:theme as a fallback at all times for legacy reasons */
                    VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the context if enabled */
            );
    	
    // 	LayoutInflaterCompat.setFactory2(layoutInflater, this); 
    //  那么 this 就是  setFactory2(
       //         @NonNull LayoutInflater inflater, @NonNull LayoutInflater.Factory2 factory) 
       //      IMPL.setFactory2(inflater, factory) 的实现
    // 
    		
    		   @Override
        public void installViewFactory() {
            LayoutInflater layoutInflater = LayoutInflater.from(mContext);
            if (layoutInflater.getFactory() == null) {
                LayoutInflaterCompat.setFactory2(layoutInflater, this);
            } else {
                if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImplV9)) {
                    Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
                            + " so we can not install AppCompat's");
                }
            }
         }
        }

    AppCompatDelegateImplV9  中重写 factory2的 createView方法, 这里回调,等待实例化 factory2  调用 createView

     AppCompatDelegateImplV9 的  createView方法 内部 AppCompatViewInflater 调用  createView  控件 替换 如何下

     public final View createView(View parent, final String name, @NonNull Context context,
                @NonNull AttributeSet attrs, boolean inheritContext,
                boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
            final Context originalContext = context;
    
            // We can emulate Lollipop's android:theme attribute propagating down the view hierarchy
            // by using the parent's context
            if (inheritContext && parent != null) {
                context = parent.getContext();
            }
            if (readAndroidTheme || readAppTheme) {
                // We then apply the theme on the context, if specified
                context = themifyContext(context, attrs, readAndroidTheme, readAppTheme);
            }
            if (wrapContext) {
                context = TintContextWrapper.wrap(context);
            }
    
            View view = null;
    
            // We need to 'inject' our tint aware Views in place of the standard framework versions
            switch (name) {
                case "TextView":
                    view = new AppCompatTextView(context, attrs);
                    break;
                case "ImageView":
                    view = new AppCompatImageView(context, attrs);
    			}
    		}
    
       @Override
        public void setContentView(int resId) {
            ensureSubDecor();
            ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
            contentParent.removeAllViews();
    		// 这里是单例
            LayoutInflater.from(mContext).inflate(resId, contentParent);
            mOriginalWindowCallback.onContentChanged();
        }

    2.4.  那么什么时候  实例化 factory2  调用 createView,AppCompatDelegateImplV9  中重写 factory2的 createView方法 什么时候调用 ??  看  LayoutInflater 源码 

    创建标签的时候 mFactory2.onCreateView, 那么 在 AppCompatViewInflater中的 createView被 调用,替换创建的标签,上面是钩子

        public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
            final Resources res = getContext().getResources();
            if (DEBUG) {
                Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                        + Integer.toHexString(resource) + ")");
            }
    
            final XmlResourceParser parser = res.getLayout(resource);
            try {
    		// 开始解析代码 
                return inflate(parser, root, attachToRoot);
            } finally {
                parser.close();
            }
        }
    
    public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    	 // 解析 标签
    	   rInflate(parser, root, inflaterContext, attrs, false);
    	 }
    
    	// 核心代码区
    	 void rInflate(XmlPullParser parser, View parent, Context context,
                AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
    			// 
    			       final View view = createViewFromTag(parent, name, context, attrs);
    			}
    			
    			
    // 根据反射创建标签
    	View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
                boolean ignoreThemeAttr) {
    			//  AppCompatViewInflater设置了 mFactory!=null 
    			  try {
                View view;
                if (mFactory2 != null) {
    			// 这里调用了 mFactory2.onCreateView 内部实现是 
    			// AppCompatViewInflater 中 createView 方法实现 拦截,拦截View的 创建 
                    view = mFactory2.onCreateView(parent, name, context, attrs);
                } else if (mFactory != null) {
                    view = mFactory.onCreateView(name, context, attrs);
                } else {
                    view = null;
                }
    
                if (view == null && mPrivateFactory != null) {
                    view = mPrivateFactory.onCreateView(parent, name, context, attrs);
                }
    
                if (view == null) {
                    final Object lastContext = mConstructorArgs[0];
                    mConstructorArgs[0] = context;
                     try {
    				//  系统的View  
                        if (-1 == name.indexOf('.')) {
                            view = onCreateView(parent, name, attrs);
                        } else {
    					// 表示自定义 View 
                            view = createView(name, null, attrs);
                        }
                    } finally {
                        mConstructorArgs[0] = lastContext;
                    }
                }
    
                return view;
            } catch (InflateException e) {
                throw e;
    			
    			}
    		    	}
    		        	}
    
    
    public final View createView(String name, String prefix, AttributeSet attrs)
                throws ClassNotFoundException, InflateException {
    			
    			   Constructor<? extends View> constructor = sConstructorMap.get(name);
            if (constructor != null && !verifyClassLoader(constructor)) {
                constructor = null;
                sConstructorMap.remove(name);
            }
            Class<? extends View> clazz = null;
    
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
           // 从 构造器中 获取View,如果存在,那么直接获取 ,
    	   // 不存在,通过反射创建 View ,存入 Map中 , 比如ImageView创建一次即可 
                if (constructor == null) {
                    // Class not found in the cache, see if it's real, and try to add it
                    clazz = mContext.getClassLoader().loadClass(
                            prefix != null ? (prefix + name) : name).asSubclass(View.class);
    
                    if (mFilter != null && clazz != null) {
                        boolean allowed = mFilter.onLoadClass(clazz);
                        if (!allowed) {
                            failNotAllowed(name, prefix, attrs);
                        }
                    }
                    constructor = clazz.getConstructor(mConstructorSignature);
                    constructor.setAccessible(true);
                    sConstructorMap.put(name, constructor);
                } else {
                    // If we have a filter, apply it to cached constructor
                    if (mFilter != null) {
                        // Have we seen this name before?
                        Boolean allowedState = mFilterMap.get(name);
                        if (allowedState == null) {
                            // New class -- remember whether it is allowed
                            clazz = mContext.getClassLoader().loadClass(
                                    prefix != null ? (prefix + name) : name).asSubclass(View.class);
    
                            boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
                            mFilterMap.put(name, allowed);
                            if (!allowed) {
                                failNotAllowed(name, prefix, attrs);
                            }
                        } else if (allowedState.equals(Boolean.FALSE)) {
                            failNotAllowed(name, prefix, attrs);
                        }
                    }
                }
    			
    			}

         LayoutInflater 中加载  来源, 是系统的一个服务,系统服务注册 , SystemServiceRegistry:

    	// 获取系统服务的时候从 Map中获取即可  
    		// 系统服务 SYSTEM_SERVICE_NAMES 保存到这个Map中  
    
    			 registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
                    new CachedServiceFetcher<LayoutInflater>() {
                @Override
                public LayoutInflater createService(ContextImpl ctx) {
                    return new PhoneLayoutInflater(ctx.getOuterContext());
                }});
    	
    	
    				  private static <T> void registerService(String serviceName, Class<T> serviceClass,
                ServiceFetcher<T> serviceFetcher) {
            SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
            SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
        }

    动态换肤: 既然上面  AppCompatActivity 可以把ImageView 替换 AppCompatImageView,通过设置, 
    那么我们也可以重写factory  来拦截View 的创建, 实现动态换肤恭喜

    3  动态换肤原理:

     换肤原理:

    任何一个apk中,资源文件  color , mip , drawable ....  
    android:textColor="?android:colorPrimary"
    android:textColor="@color/text_color"
    android:background="@mipmap/btn"
    只要是名称 res_name 相同 ,那么任何一个apk 生成的 id(索引值是相同的) ,那么我们只需要把资源放入 一个 skin1.apk中即可,和主apk res_name相同,图片名称相同,颜色 名称相同 在color.xml 下

    然后读取主 apk 控件属性对应 的value 的 int值   ,去资源apk中 找到对应资源即可,替换主apk中资源

     

    只要是名称 res_name 相同 ,那么任何一个apk 生成的 id(索引值是相同的),如何理解: 看图

    主apk resourec资源文件:

    皮肤skin.apk  resourec资源文件:

    主apk 颜色资源文件

    skin 换肤 apk 颜色资源文件

    然后读取主 apk 控件属性对应 的value 的 int值   ,去资源apk中 找到对应资源即可

    资源文件替换代码:

    1. 首先 创建一个 skin.apk ,如图 

     2.  adb push  skinp\build\outputs\apk\debug\red.apk /sdcard  

    测试 资源替换代码 如何 :

      /**
         * 测试皮肤替换 :  adb push  skinp\build\outputs\apk\debug\red.apk /sdcard
         * @param view
         */
        public void testSkin(View view) {
            try {
                // 加载系统下皮肤
                String skinPath= Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator +"red.apk";
                File file=new File(skinPath);
                if(!file.exists()){
                    Log.e(Tag,"文件不存在");
                    return;
                }
                AssetManager asset  = AssetManager.class.newInstance();
                // 读取 本地一个  .skin 里面资源
                Resources  superRes = getResources();
                // 添加本地下载好的资源皮肤    Native 层 c和 c++ 怎么搞得
                Method method = AssetManager.class.getDeclaredMethod("addAssetPath",String.class);
                method.setAccessible(true);
                // 执行反射方法
                method.invoke(asset,skinPath);
                mSkinResources=new Resources(asset,superRes.getDisplayMetrics(),superRes.getConfiguration());
                packageName= getPackageManager().getPackageArchiveInfo(skinPath,PackageManager.GET_ACTIVITIES).packageName ;
                Log.e(Tag, "packageName:"+packageName);
    
                // 根据 包名, 资源文件名称 , 在 drawabel目录下 , 获取id 值 对应的  0x7f06000054
                int drawableId=  mSkinResources.getIdentifier("btn", "drawable",packageName);
    
                // 获取 颜色的 id值
                int colorId=  mSkinResources.getIdentifier("text_color","color",packageName);
    
                Log.e(Tag, "packageName:"+packageName + "   drawable:"+ drawableId);
                // 根据id值  获取到 skin.apk 中 drawable对象
    //            Drawable drawable=ContextCompat.getDrawable(this,drawableId);  // 获取自己目录下
    
                Drawable drawable=mSkinResources.getDrawable(drawableId);
    
                // 根据id 值获取到颜色
                ColorStateList colorStateList =  mSkinResources.getColorStateList(colorId);
    
    
                // 修改控件
                showImg.setBackground(drawable);
                showText.setTextColor(colorStateList);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
    
        }

    综上  换肤框架实现 源码:

    
    package mk.denganzhi.com.zhiwenku;
    import android.Manifest;
    import android.app.Activity;
    import android.content.Context;
    import android.content.pm.PackageManager;
    import android.content.res.AssetManager;
    import android.content.res.ColorStateList;
    import android.content.res.Resources;
    import android.graphics.drawable.Drawable;
    import android.os.Environment;
    import android.support.v4.app.ActivityCompat;
    import android.support.v4.content.ContextCompat;
    import android.support.v4.view.LayoutInflaterCompat;
    import android.support.v4.view.LayoutInflaterFactory;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.support.v7.widget.AppCompatImageView;
    import android.text.TextUtils;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    import java.io.File;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity {
    
    
      String Tag="denganzhi";
    
    
        private static final List<String> mAttributes = new ArrayList<>();
        static {
            mAttributes.add("background");
            mAttributes.add("src");
    
            mAttributes.add("textColor");
            mAttributes.add("drawableLeft");
            mAttributes.add("drawableTop");
            mAttributes.add("drawableRight");
            mAttributes.add("drawableBottom");
    
            mAttributes.add("skinTypeface");
        }
    
      List<SkinView> skinViews=new ArrayList<>();
      ImageView showImg=null;
      TextView showText=null;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
    
            /**
             *  LayoutInflater LayoutInflater =
             (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
             LayoutInflater  是一个系统服务, 单例
             */
              LayoutInflater layoutInflater=  LayoutInflater.from(this);
    
    
    //        //  setFactory
    //        LayoutInflaterCompat.setFactory(layoutInflater, new LayoutInflaterFactory() {
    //            @Override
    //            public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
    //                Log.e("denganzhi1","创建View被拦截:"+name);
    //
    //
    //                // 1. 创建View
    //
    //                // 2. 解析属性
    //
    //                // 3.  同意交给SkinManager管理
    //
    //                if(name.equals("ImageView")){
    //                    TextView textView=new TextView(MainActivity.this);
    //                    textView.setText("拦截");
    //                    return textView;
    //                }
    //                return null;
    //            }
    //        });
    
    
    
    
    
            //  setFactory2
            LayoutInflaterCompat.setFactory2(layoutInflater, new LayoutInflater.Factory2() {
                @Override
                public View onCreateView(String name, Context context, AttributeSet attrs) {
    
                    Log.e("denganzhi1","创建View被拦截1:"+name);
                    return null;
                }
    
                @Override
                public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
                    // 反射 classLoader
                    View view = createViewFromTag(name, context, attrs);
                    // 自定义View
                    if(null ==  view){
                        view = createView(name, context, attrs);
                    }
    
    
                    SkinView skinView=new SkinView();  // 控件集合
                    skinView.setView(view);
    
                    List<SkinAttr> skinAttrs=new ArrayList<>();   // 每一个控件存放属性的集合
                    int attrLength= attrs.getAttributeCount();
                    for (int index=0;index<attrLength; index++){
                        // 获取名称,值
                        String attrName = attrs.getAttributeName(index);
                        //  不需要换肤 属性值
                        if (!mAttributes.contains(attrName)) {
                            continue;
                        }
                        // 不符合 换肤条件
                        String attrValue = attrs.getAttributeValue(index);
                        if (attrValue.startsWith("#")) {
                            continue;
                        }
                        /**
                         *
                         * android:background="?android:colorPrimary"   使用系统颜色值      ?16843827
                         android:background="#000000"       不符合换肤条件
                         android:background="@mipmap/ic_launcher"   使用之定义    @2131361793
                         所有的 value 都会别转化为  int 值
                         */
                   //  attrName:background  attValue:@2131361792
    
                        int resId = 0 ;
                        if (attrValue.startsWith("@") ||  attrValue.startsWith("?")) {
                            attrValue = attrValue.substring(1);
                            resId=   Integer.parseInt(attrValue);
                        }
    
                        if(resId==0){   //不符合条件
                            continue;
                        }
    
    
                        SkinAttr skinAttr =new SkinAttr();
                        skinAttr.setSkyType(attrName);
                    //    skinAttr.setmResName(resName);
                        skinAttr.setmResId(resId);
                        skinAttrs.add(skinAttr);
                    }
                     if(skinAttrs.size()>0 ){
                         skinView.setViewName(name);
                         skinView.setmAttrs(skinAttrs);  // 添加属性集合
                         skinViews.add(skinView);        // 添加View
                         Log.e(Tag,"添加的View:"+ name + "   skinView: "+ skinView );
                     }
    
    
    //                if(name.equals("ImageView")){
    //                    TextView textView=new TextView(MainActivity.this);
    //                    textView.setText("拦截");
    //                    return textView;
    //                }
    
                    return view;
                }
            });
    
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            showImg = findViewById(R.id.showImg);
            showText = findViewById(R.id.showText);
    
    
            if (ContextCompat.checkSelfPermission(this,
                    Manifest.permission.READ_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED)
            {
    
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                        110);
            } else
            {
    
            }
    
    //        myImageView = findViewById(R.id.myImageView);
    //        Log.e("denganzhi",myImageView.getClass().toString());
        }
    
    
        // 比如根据 @123456 获取 颜色图片 名称
        public  String getResName(String attrValue) {
            // 如果   android:background="#ffffff"   那么过滤掉,不换皮肤
            if (attrValue.startsWith("@") ||  attrValue.startsWith("?")) {
                attrValue = attrValue.substring(1);
                // 根据id 去插件包中找  资源
                int resId = Integer.parseInt(attrValue);
    
                // 获取资源类型
                String resourceTypeName =  getResources().getResourceTypeName(resId);
                Log.e(Tag,"resourceTypeName: " +resourceTypeName);
    
                //  通过id 获取资源名字
    
                return  getResources().getResourceEntryName(resId);
    
            }
    
            return  null;
    
        }
    
    
        // 比如根据 @123456 获取 颜色图片 名称   btn_bg
        public  String getResNamebyResId(int  resId) {
            // 如果   android:background="#ffffff"   那么过滤掉,不换皮肤
                //  通过id 获取资源名字
                return  getResources().getResourceEntryName(resId);
        }
        // 类型 mip 、color、drawable、layout.....
        public String  getResTypebyResId(int resId){
            // 获取资源类型
            String resourceTypeName =  getResources().getResourceTypeName(resId);
            return  resourceTypeName;
        }
    
    
    
        Resources  mSkinResources =null;
        String packageName=null;
        public void changeSkin(View view) {
    
            Log.e(Tag,"换肤List:"+skinViews);
    
            try {
                // 加载系统下皮肤
                String skinPath= Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator +"red.apk";
                File file=new File(skinPath);
                if(!file.exists()){
                    Log.e(Tag,"文件不存在");
                    return;
                }
                AssetManager asset  = AssetManager.class.newInstance();
                // 读取 本地一个  .skin 里面资源
                Resources  superRes = getResources();
                // 添加本地下载好的资源皮肤    Native 层 c和 c++ 怎么搞得
                Method method = AssetManager.class.getDeclaredMethod("addAssetPath",String.class);
                method.setAccessible(true);
                // 执行反射方法
                method.invoke(asset,skinPath);
                mSkinResources=new Resources(asset,superRes.getDisplayMetrics(),superRes.getConfiguration());
                // 获取资源文件的包名
                packageName= getPackageManager().getPackageArchiveInfo(skinPath,PackageManager.GET_ACTIVITIES).packageName ;
    
    
    
                if(!TextUtils.isEmpty(packageName)  && mSkinResources!=null ){
                    for(int i=0;i<skinViews.size();i++){
                        SkinView skinView= skinViews.get(i);
                        View viewSkin= skinView.getView();
                        List<SkinAttr> skinAttrs= skinView.getmAttrs();
                        for (int j=0;j<skinAttrs.size();j++){
    
                            SkinAttr skinAttr= skinAttrs.get(j);
                            String skyType= skinAttr.getSkyType();
                            int mResId= skinAttr.getmResId();
    
                            String resName= getResNamebyResId(mResId);
                            String resType = getResTypebyResId(mResId);
    
    
                          if(TextUtils.isEmpty(resName)){  // 没有找到
                            continue;
                          }
                            // attrName:textColor  attValue:@2130968660  resName:text_color
                            // attrName:textColor  attValue:?16843827   resName:colorPrimary
    
    
                            Log.e(Tag, "packageName:"+packageName+"  resName:"+resName  + "   resType:"+ resType);
    
                            // 需要换肤的背景  background
                            if(skyType.equals("background")){
    
                                 // 背景可以是图片  也可能是颜色
                                if(resType.equals("color")){
                                    int background=  mSkinResources.getColor(mResId);
                                    viewSkin.setBackgroundColor((Integer) background);
                                }else{
                                    Drawable drawable= getDrawableByName(resName);
                                    ImageView imageView= (ImageView) viewSkin;
                                    imageView.setBackground(drawable);
                                }
    
                             // 需要换肤的 textcolor
                            }else if(skyType.equals("textColor")){
    
                                //     int colorId=  mSkinResources.getIdentifier("text_color","color",packageName);
                                //        ColorStateList colorStateList=  mSkinResources.getColorStateList(resId);
                                ColorStateList colorStateList=  getColorByName(resName);
                                TextView textView= (TextView)viewSkin;
                                textView.setTextColor(colorStateList);
                            }else if(skyType.equals("src")){
    
                            }else if(skyType.equals("skinTypeface")){
    
                                 // 如何修改字体,思路
    
                                // 可以判断这里是 viewSkin 是TextView Button 然后设置  ,  第二个  参数传递不同路径即可
                                //  textView.setTypeface( Typeface.createFromAsset(this.getAssets(), "fonts/CodeBold.ttf"));
                            }
                        }
                    }
                }
    
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
    
        }
    
      
    
        public ColorStateList getColorByName(String resName){
            int resId= mSkinResources.getIdentifier(resName,"color",packageName);
            ColorStateList colorStateList=  mSkinResources.getColorStateList(resId);
            Log.e(Tag,"colorId:"+resId);
            return  colorStateList;
        }
    
        public void restoreSkin(View view)  {
    
    
        }
    
    
        private static final String[] mClassPrefixlist = {
                "android.widget.",
                "android.view.",
                "android.webkit."
        };
        private View createViewFromTag(String name, Context context, AttributeSet attrs) {
            //包含自定义控件
            if (-1 != name.indexOf(".")) {
                return null;
            }
            //
            View view = null;
            for (int i = 0; i < mClassPrefixlist.length; i++) {
                view = createView(mClassPrefixlist[i] + name, context, attrs);
                if(null != view){
                    break;
                }
            }
            return view;
        }
        private static final Class[] mConstructorSignature =
                new Class[]{Context.class, AttributeSet.class};
        private static final HashMap<String, Constructor<? extends View>> mConstructor =
                new HashMap<String, Constructor<? extends View>>();
        private View createView(String name, Context context, AttributeSet attrs) {
            Constructor<? extends View> constructor = mConstructor.get(name);
            if (constructor == null) {
                try {
                    //通过全类名获取class
                    Class<? extends View> aClass = context.getClassLoader().loadClass(name).asSubclass(View.class);
                    //获取构造方法
                    constructor = aClass.getConstructor(mConstructorSignature);
                    mConstructor.put(name, constructor);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (null != constructor) {
                try {
                    return constructor.newInstance(context, attrs);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    
    
    }
    

     SkinView.java

    package mk.denganzhi.com.zhiwenku;
    
    import android.view.View;
    
    import java.util.List;
    
    /**
     * Created by Administrator on 2020/5/24.
     */
    
    public class SkinView {
    
        private View view;
        private String viewName;
    
        private List<SkinAttr> mAttrs;
    
        public View getView() {
            return view;
        }
    
        public void setView(View view) {
            this.view = view;
        }
    
        public List<SkinAttr> getmAttrs() {
            return mAttrs;
        }
    
        public String getViewName() {
            return viewName;
        }
    
        public void setViewName(String viewName) {
            this.viewName = viewName;
        }
    
        public void setmAttrs(List<SkinAttr> mAttrs) {
            this.mAttrs = mAttrs;
        }
    
        @Override
        public String toString() {
            return "SkinView{" +
                    "view=" + view +
                    ", viewName='" + viewName + '\'' +
                    ", mAttrs=" + mAttrs +
                    '}';
        }
    }
    
    SkinAttr.java
    
    public class SkinAttr {
    
        private String mResName;
        private String skyType;
        private  int mResId;
    
    
    
    
        public String getmResName() {
            return mResName;
        }
    
        public void setmResName(String mResName) {
            this.mResName = mResName;
        }
    
        public String getSkyType() {
            return skyType;
        }
    
        public void setSkyType(String skyType) {
            this.skyType = skyType;
        }
    
        public int getmResId() {
            return mResId;
        }
    
        public void setmResId(int mResId) {
            this.mResId = mResId;
        }
    
        @Override
        public String toString() {
            return "SkinAttr{" +
                    "mResName='" + mResName + '\'' +
                    ", skyType='" + skyType + '\'' +
                    ", mResId=" + mResId +
                    '}';
        }
    }
    

    源码地址:https://download.csdn.net/download/dreams_deng/12454900

    展开全文
  • Token的机制

    千次阅读 2015-03-17 16:28:40
    近期来准备把原来做的thinkphp项目中写的假token利用thinkphp自带的getToken写成动态的,但是遇见了很大的问题, 感觉主要还是对cookie,session,token原理以及使用方法不太了解,下转载一篇帖子来明确一下思路...

    近期来准备把原来做的thinkphp项目中写的假token利用thinkphp自带的getToken写成动态的,但是遇见了很大的问题,

    感觉主要还是对cookie,session,token的原理以及使用方法不太了解,下转载一篇帖子来明确一下思路。

    利用Token机制可以解决表单重复提交问题。实质是服务器端每次都生成一个不同的Token值返回给客户端,同时保存在session里。客户端提交表单时,必须把此token值提交给服务器。程序判断存储在session中的Token值和请求参数中的Token值是否一致。不一致说明本次操作已经被提交过了。

     

     

    Token机制

    1、什么是表单重复提交

    如:对于注册表单,若用户已提交表单且服务器端成功注册了用户信息。此时用户通过浏览器回退功能,回到原来页面重复提交表单,服务器端应避免用户重复注册。

    还有一种情形:提交完成后,单击浏览器的“刷新”按钮,浏览器会弹出对话框,询问是否重新提交数据。单击“是”,浏览器会重新提交数据。 

     

    2、Struts1的Token机制避免重复提交表单的流程

    (1)在请求某个页面时,先进入Action,调用Action的saveToken()方法,创建一个新的Token,将其保存在session中,然后将请求转发给此页面。

    (2)页面中的<html:form>标签的处理类判断在session中是否存在Token,若存在,就在表单中生成一个包含Token信息的隐藏字段。当用户收到此页面后,查看源文件可以发现表单中有一个包含Token信息的隐藏字段。

    (3)当用户提交表单后,进入Action,首先调用Action的isTokenValid()方法,判断当前session中的Token值和请求参数中的Token值是否一致。不一致方法返回false。若一致,方法中会调用resetToken()方法,从当前session中删除Token,且方法最后返回true。

    这样,在用户提交了表单之后,通过浏览器回退功能,退回到刚才的页面,再次提交表单,由于当前session中不存在Token,则返回false。


    在Struts1的Action中提供了和同步令牌相关的方法:

    protectedboolean isTokenValid(HttpServletRequest request)

    此方法用来判断当前用户会话中存储的令牌值和当前请求参数的令牌值是否一致,如果不一致返回false,一致则返回true。当我们利用Action进行相应调用处理时,就是利用此方法判断令牌值是否一致来决定是否进行处理。

      

      protected void saveToken(HttpServletRequest request)

    此方法创建一个新的令牌值,将其保存在当前的会话(session)范围内。如果当前的会话对象不存在,则创建会话对象。

      

      protected void resetToken(HttpServletRequest request)

    此方法对当前会话范围的令牌值进行复位操作,即删除当前会话范围的令牌值。

     

    事实上当执行了this.saveToken之后系统自动的在所有带form表单的JSP页面的form表单代码的里面加入了隐藏域input标签代码。

    当用户提交时,控制器获取隐藏域的内容,利用isValidToken方法判断此参数内容是否和会话中存储的令牌值内容匹配。

    第一次提交之后一定是匹配的,但是紧接着它就把session中的令牌值删除。再点回退时,隐藏域的内容仍然是原来的值,显然再次提交时必然不会匹配的。


    展开全文
  • 昨天学会了动态获取token,今天测试接口时希望能够实现跨线程调用token值。 实现原理: jmeter本身的“__setProperty”函数可以把某参数的值设置成jmeter本身的内置属性,而jmeter的内置属性是所有线程组都可以...
  • xhprof就在php被加载  xhprof的工作原理  xhprof是php的第三方扩展,工作在zend层,以动态加载的方式被加载。php在解释执行的过程中,首先被解释成对应的token码,而后,编译成字节码(这里的字节码不是机器码,在...
  • 微博三方登录原理

    2020-10-09 18:55:28
    1. 微博三方登录流程 1.1 前端获取认证code 在Vue页面加载时 动态发送请求获取微博授权url django收到请求的url后,通过微博 应用ID(client_id)和回调地址...1.2 获取微博access_token 后端获取code后,结合clie
  • 微博三方登录流程 1.1 前端获取认证code 1.在Vue页面加载时 动态发送请求获取微博授权url 2.django收到请求的url后,通过微博 应用ID(client_id)和回调地址(redirect_uri) ...1.2 获取微博access_token 后端获取co
  • Final Project of Compiler Principle ...分步执行将一个token一个token对代码进行,分析结束之后需要手动选择File-Save选择输出结果的路径。 一次性执行将动态的展示代码分析过程,最后将自动弹出对话框选择保存路径。
  • 微信登录获取扫码结果的原理

    千次阅读 2016-08-05 16:57:48
    用户访问网页版登录界面,登录页面动态生成一个登录链接,应该带有token之类的,这时候网页开始不停的给服务器发送请求,轮训用户是否已经扫码成功。用户用微信客户端扫码,扫码之后微信客户端向服务器发送一个请求...
  • JWT 的动态刷新

    2020-12-07 10:35:16
    有关jwt的定义资料没有具体写,关于具体的原理及理论点,想了解的可以点击下面两个连接去了解,写的不错,比较完善。 什么是 JWT – JSON WEB TOKEN. 认识JWT. 使用过程中遇到的问题 这里主要讲一下,JWT的动态刷新...
  • 存储过程-动态批量更新数据

    千次阅读 2019-05-07 20:16:33
    原理 自定义类型及函数解析指定格式字符串, 返回table类型数据 动态sql拼接待更新数据过滤条件 实现 自定义类型 -- create base type CREATE OR REPLACE TYPE SPLITSTRINGROW_UNI_TYPE AS OBJECT ( ID NUMBER(5...
  • xhprof的工作原理  xhprof是php的第三方扩展,工作在zend层,以动态加载的方式被加载。php在解释执行的过程中,首先被解释成对应的token码,而后,编译成字节码(这里的字节码不是机器码,在PHP里面...
  • 对于一个初学者来说,使用框架是一件再简单快乐的事情不过了,但是框架隐的细节实在是太多了,想要用好框架,阅读源码是必不可少的事情,这次刨析可以省去大家来回查找动态绑定的子类过程,也作为自己学习的一次笔记...
  • Systemview是美国ELANIX公司于1995年开始推出的软件工具,它为用户提供了一个完整的动态系统设计、仿真与分析的可视化软件环境,能进行模拟、数字、数模混合系统、线性和非线性系统的分析设计,可对线性系统进行拉氏...
  • 登录凭证的方式(公司使用cookie)

    千次阅读 2015-09-11 16:05:34
    1,登录之前访问 认证系统的动态口令接口,获取动态口令:auth_token 同时会返回cookie:CCC ,目的:把浏览器和 auth_token 挂钩 等浏览器访问其他应用时,把该CCC带过去. 2,登录时参数包括(1)中返回的auth_token ; 3,...
  • 关联原理:在录制时,服务器会给我们一个唯一的认证码来进行操作,但是我们录制的时候只是获取到了当前的码,是固定的,不是动态的,后面提交认证的时候会导致脚本执行失败,所以我们需要动态的实时获取最新的认证码...
  • 文章目录1、微博三方登录原理1.1 前端获取认证code1.2 获取微博access_token1.3 获取微博用户基本信息并保存到数据库1.4 生成token给Vue2.第三方登录与本地登录的关联(三种情况)3.oauth认证原理4.为什么使用三方...
  • 2,使用Token可以解决重复提交的问题,Token的运行原理就是根据设置session属性范围来操作的。 3,在Struts中使用FileUpload组件完成文件上传,通过标签可以指定上传文件,而通过FormFile类可以接收上传文件。 ...
  • Spring Cloud Alibaba学习笔记Sentinel流量防卫兵热点参数限流概念动态设置参数例外项代码设置系统自适应限...Sentinel Dashboard修改微服务应用集群流控概述为什么需要集群流控集群流控原理Token Server 启动模式独立
  • 在开展接口测试或者是接口面试的过程中,我们会发现很多接口需要依赖前面的接口,需要我们动态从前面的接口返回中提取数据,也就是我们通常说的关联。 关联通俗来讲就是把上一次请求的返回内容中的部分截取出来保存...
  • Google Authenticator相当于软token,对他不了解的同学可以看下这篇文章:谷歌验证 (Google Authenticator) 的实现原理是什么?。 运维同学的出发点是好的,但是我原来写的各种自动登录服务器的脚本统统失效了。...
  • Google Authenticator相当于软token,对他不了解的同学可以看下这篇文章:谷歌验证 (Google Authenticator) 的实现原理是什么?。 运维同学的出发点是好的,但是我原来写的各种自动登录服务器的脚本统统失效了。蛋疼...
  • Android 面试题

    2016-12-05 23:10:39
    4.Android中的碎片动态管理 5.APP崩溃率 6.热修复实现原理 7.Android的65k问题 8.Android的Dex分包机制 9.不同界面跳转到登录界面怎样防止token丢失 10.谈谈你对敏捷开发的理解 11.后台php是如何加密的 12....
  • ImageSee 1.0源码

    2006-02-23 09:05:59
    5.主要技巧算法: (1)缩略图 实现原理动态创建Tbitmap对象,设定大小并画上两个边框使其像一个panel,最后把按比例缩小后的图片bitblt于其上。 (2)语法高亮方式显示.pas文件 实现原理:该功能其实就是编译原理...

空空如也

空空如也

1 2 3 4 5 6
收藏数 106
精华内容 42
关键字:

动态token原理