当前位置: 首页 > news >正文

网站开发论文英文资料seo优化员

网站开发论文英文资料,seo优化员,建设银行广州招聘网站,wordpress分类链接后加目录 1.用户登录认证流程 1.1 生成认证Token 1.2 用户登录认证 1.2.1 SecurityManager login流程解析 1.2.1.1 authenticate方法进行登录认证 1.2.1.1.1 单Realm认证 1.2.1.2 认证通过后创建登录用户对象 1.2.1.2.1 复制SubjectContext 1.2.1.2.2 对subjectContext设…

目录

1.用户登录认证流程

1.1 生成认证Token

1.2 用户登录认证

1.2.1  SecurityManager login流程解析

1.2.1.1 authenticate方法进行登录认证

1.2.1.1.1 单Realm认证

1.2.1.2 认证通过后创建登录用户对象

1.2.1.2.1 复制SubjectContext

1.2.1.2.2 对subjectContext设置securityManager

1.2.1.2.3 对subjectContext设置session

1.2.1.2.4 对subjectContext设置Principals

1.2.1.2.5 根据subjectContext创建Subject

1.2.1.2.6 保存Subject 


Shiro作为一款比较流行的登录认证、访问控制安全框架,被广泛应用在程序员社区;Shiro登录验证、访问控制、Session管理等流程内部都是委托给SecurityManager安全管理器来完成的,SecurityManager安全管理器上篇文章已经进行了详细解析,详见:Shiro框架:Shiro SecurityManager安全管理器解析-CSDN博客,在此基础上,本篇文章继续对Shiro关联链路处理流程之一---登录认证流程 进行解析;

想要深入了解Shiro框架整体原理,可移步:

Shiro框架:ShiroFilterFactoryBean过滤器源码解析-CSDN博客、

Shiro框架:Shiro内置过滤器源码解析-CSDN博客

1.用户登录认证流程

在Shiro框架:Shiro内置过滤器源码解析-CSDN博客内置过滤器分析中,我们知道用户执行登录的认证操作是在过滤器FormAuthenticationFilter中执行的,如下:

    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {if (isLoginRequest(request, response)) {if (isLoginSubmission(request, response)) {if (log.isTraceEnabled()) {log.trace("Login submission detected.  Attempting to execute login.");}return executeLogin(request, response);} else {if (log.isTraceEnabled()) {log.trace("Login page view.");}//allow them to see the login page ;)return true;}} else {if (log.isTraceEnabled()) {log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +"Authentication url [" + getLoginUrl() + "]");}saveRequestAndRedirectToLogin(request, response);return false;}}

登录认证操作是在executeLogin方法中完成的:

    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {AuthenticationToken token = createToken(request, response);if (token == null) {String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +"must be created in order to execute a login attempt.";throw new IllegalStateException(msg);}try {Subject subject = getSubject(request, response);subject.login(token);return onLoginSuccess(token, subject, request, response);} catch (AuthenticationException e) {return onLoginFailure(token, e, request, response);}}

上述源码的执行过程表示为时序图会更直观,时序图如下:

该认证过程主要包含以下几个部分:

  1. 根据用户名密码等生成认证Token
  2. 获取当前登录用户Subject
  3. 调用Subject的login方法进行登录认证
  4. 其它的登录成功、或登录失败的拦截方法

下面主要对生成认证Token和用户登录认证实现进行详细说明;

1.1 生成认证Token

createToken的实现在FormAuthenticationFilter内,如下:

这里用户名和密码是通过request请求对象获取的:

    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {String username = getUsername(request);String password = getPassword(request);return createToken(username, password, request, response);}

在父类AuthenticatingFilter中进一步实现如下,这里构造了UsernamePasswordToken类,表示采用用户名密码的认证方式;

    protected AuthenticationToken createToken(String username, String password,ServletRequest request, ServletResponse response) {boolean rememberMe = isRememberMe(request);String host = getHost(request);return createToken(username, password, rememberMe, host);}protected AuthenticationToken createToken(String username, String password,boolean rememberMe, String host) {return new UsernamePasswordToken(username, password, rememberMe, host);}

图示UsernamePasswordToken的继承结构:

1.2 用户登录认证

在Subject的login的具体实现如下:

  • 实际的login是委托给SecurityManager完成的
  • 登录成功后设置当前登录对象为认证成功

下面主要对SecurityManager的login方法进行具体分析; 

1.2.1  SecurityManager login流程解析

login方法具体实现如下:

    public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {AuthenticationInfo info;try {info = authenticate(token);} catch (AuthenticationException ae) {try {onFailedLogin(token, ae, subject);} catch (Exception e) {if (log.isInfoEnabled()) {log.info("onFailedLogin method threw an " +"exception.  Logging and propagating original AuthenticationException.", e);}}throw ae; //propagate}Subject loggedIn = createSubject(token, info, subject);onSuccessfulLogin(token, info, loggedIn);return loggedIn;}

上述登录流程主要包含2部分内容: 

  1. 通过authenticate方法进行登录认证
  2. 认证通过后创建登录用户对象

下面分别进行展开分析;

1.2.1.1 authenticate方法进行登录认证

具体的authenticate实现内部又委托给了认证器Authenticator来实现,具体的认证器为ModularRealmAuthenticator,其继承结构如下:

authenticate方法具体实现是在父类AbstractAuthenticator中,如下:

public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {if (token == null) {throw new IllegalArgumentException("Method argument (authentication token) cannot be null.");}log.trace("Authentication attempt received for token [{}]", token);AuthenticationInfo info;try {info = doAuthenticate(token);if (info == null) {String msg = "No account information found for authentication token [" + token + "] by this " +"Authenticator instance.  Please check that it is configured correctly.";throw new AuthenticationException(msg);}} catch (Throwable t) {AuthenticationException ae = null;if (t instanceof AuthenticationException) {ae = (AuthenticationException) t;}if (ae == null) {//Exception thrown was not an expected AuthenticationException.  Therefore it is probably a little more//severe or unexpected.  So, wrap in an AuthenticationException, log to warn, and propagate:String msg = "Authentication failed for token submission [" + token + "].  Possible unexpected " +"error? (Typical or expected login exceptions should extend from AuthenticationException).";ae = new AuthenticationException(msg, t);if (log.isWarnEnabled())log.warn(msg, t);}try {notifyFailure(token, ae);} catch (Throwable t2) {if (log.isWarnEnabled()) {String msg = "Unable to send notification for failed authentication attempt - listener error?.  " +"Please check your AuthenticationListener implementation(s).  Logging sending exception " +"and propagating original AuthenticationException instead...";log.warn(msg, t2);}}throw ae;}log.debug("Authentication successful for token [{}].  Returned account [{}]", token, info);notifySuccess(token, info);return info;}

通过方法doAuthenticate对认证Token进行认证,通过notifySuccess、notifyFailure监听认证通过或失败的事件并调用注册的监听器;

方法doAuthenticate的具体实现是在子类完成的,如下:

    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {assertRealmsConfigured();Collection<Realm> realms = getRealms();if (realms.size() == 1) {return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);} else {return doMultiRealmAuthentication(realms, authenticationToken);}}

这里首先获取已注册的安全组件Realm,其注册过程是在RealmSecurityManager初始化的过程中完成的(对SecurityManager安全管理器的处理过程感兴趣可以参见:Shiro框架:Shiro SecurityManager安全管理器解析-CSDN博客)

然后根据Realm的个数分别执行单Realm认证或多Realm认证,这里已单Realm认证为例进行说明,多Realm认证类似;

1.2.1.1.1 单Realm认证

通过doSingleRealmAuthentication方法完成单Realm认证,实现如下:

    protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {if (!realm.supports(token)) {String msg = "Realm [" + realm + "] does not support authentication token [" +token + "].  Please ensure that the appropriate Realm implementation is " +"configured correctly or that the realm accepts AuthenticationTokens of this type.";throw new UnsupportedTokenException(msg);}AuthenticationInfo info = realm.getAuthenticationInfo(token);if (info == null) {String msg = "Realm [" + realm + "] was unable to find account data for the " +"submitted AuthenticationToken [" + token + "].";throw new UnknownAccountException(msg);}return info;}

这里Reaml接口包含一整套的继承层次实现,如下,这里不过多展开解析;

getAuthenticationInfo是在AuthenticatingRealm中完成的,如下:

    public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {AuthenticationInfo info = getCachedAuthenticationInfo(token);if (info == null) {//otherwise not cached, perform the lookup:info = doGetAuthenticationInfo(token);log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);if (token != null && info != null) {cacheAuthenticationInfoIfPossible(token, info);}} else {log.debug("Using cached authentication info [{}] to perform credentials matching.", info);}if (info != null) {assertCredentialsMatch(token, info);} else {log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);}return info;}

 这里首先获取AuthenticationInfo对象,要么从缓存中获取,要么通过引入的抽象方法doGetAuthenticationInfo获取(交由应用层子类具体实现);

获取到AuthenticationInfo后,将其余用户录入的登录Token进行比对,这部分具体是方法assertCredentialsMatch完成的,具体如下:

    protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {CredentialsMatcher cm = getCredentialsMatcher();if (cm != null) {if (!cm.doCredentialsMatch(token, info)) {//not successful - throw an exception to indicate this:String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";throw new IncorrectCredentialsException(msg);}} else {throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +"credentials during authentication.  If you do not wish for credentials to be examined, you " +"can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");}}

这里获取了默认的匹配器SimpleCredentialsMatcher,并调用doCredentialsMatch方法进行匹配,匹配实现如下:

    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {Object tokenCredentials = getCredentials(token);Object accountCredentials = getCredentials(info);return equals(tokenCredentials, accountCredentials);}

首先获取Credentials,也即用户密码,然后调用equals方法进行匹配,如下通过字节流的方式进行比对(主要是为了安全考虑,系统处理时采用非明文形式)。

    protected boolean equals(Object tokenCredentials, Object accountCredentials) {if (log.isDebugEnabled()) {log.debug("Performing credentials equality check for tokenCredentials of type [" +tokenCredentials.getClass().getName() + " and accountCredentials of type [" +accountCredentials.getClass().getName() + "]");}if (isByteSource(tokenCredentials) && isByteSource(accountCredentials)) {if (log.isDebugEnabled()) {log.debug("Both credentials arguments can be easily converted to byte arrays.  Performing " +"array equals comparison");}byte[] tokenBytes = toBytes(tokenCredentials);byte[] accountBytes = toBytes(accountCredentials);return MessageDigest.isEqual(tokenBytes, accountBytes);} else {return accountCredentials.equals(tokenCredentials);}}

 至此,用户账号密码匹配完成,匹配完成后,会重新创建用户登录对象,并更新用户状态等,下面具体分析;

1.2.1.2 认证通过后创建登录用户对象

认证通过后创建Subject的实现如下:

    protected Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing) {SubjectContext context = createSubjectContext();context.setAuthenticated(true);context.setAuthenticationToken(token);context.setAuthenticationInfo(info);if (existing != null) {context.setSubject(existing);}return createSubject(context);}@Overrideprotected SubjectContext createSubjectContext() {return new DefaultWebSubjectContext();}

这里创建了DefaultWebSubjectContext,用户Subject创建,其中设置了认证状态、认证Token、已认证信息,然后调用createSubject方法继续进行构造,如下:

    public Subject createSubject(SubjectContext subjectContext) {//create a copy so we don't modify the argument's backing map:SubjectContext context = copy(subjectContext);//ensure that the context has a SecurityManager instance, and if not, add one:context = ensureSecurityManager(context);//Resolve an associated Session (usually based on a referenced session ID), and place it in the context before//sending to the SubjectFactory.  The SubjectFactory should not need to know how to acquire sessions as the//process is often environment specific - better to shield the SF from these details:context = resolveSession(context);//Similarly, the SubjectFactory should not require any concept of RememberMe - translate that here first//if possible before handing off to the SubjectFactory:context = resolvePrincipals(context);Subject subject = doCreateSubject(context);//save this subject for future reference if necessary://(this is needed here in case rememberMe principals were resolved and they need to be stored in the//session, so we don't constantly rehydrate the rememberMe PrincipalCollection on every operation).//Added in 1.2:save(subject);return subject;}

如上创建过程包含了以下几部分,分别进行分析;

1.2.1.2.1 复制SubjectContext

对subjectContext调用复制构造方法进行复制,subjectContext底层通过backingMap保存上下文信息:

    public MapContext(Map<String, Object> map) {this();if (!CollectionUtils.isEmpty(map)) {this.backingMap.putAll(map);}}
1.2.1.2.2 对subjectContext设置securityManager

获取securityManager的顺序如下:

  1. 首先从subjectContext中获取
  2. 若无,则从当前线程上下文中获取
  3. 否则将当前securityManager设置到subjectContext中
    protected SubjectContext ensureSecurityManager(SubjectContext context) {if (context.resolveSecurityManager() != null) {log.trace("Context already contains a SecurityManager instance.  Returning.");return context;}log.trace("No SecurityManager found in context.  Adding self reference.");context.setSecurityManager(this);return context;}
    public SecurityManager resolveSecurityManager() {SecurityManager securityManager = getSecurityManager();if (securityManager == null) {if (log.isDebugEnabled()) {log.debug("No SecurityManager available in subject context map.  " +"Falling back to SecurityUtils.getSecurityManager() lookup.");}try {securityManager = SecurityUtils.getSecurityManager();} catch (UnavailableSecurityManagerException e) {if (log.isDebugEnabled()) {log.debug("No SecurityManager available via SecurityUtils.  Heuristics exhausted.", e);}}}return securityManager;}
1.2.1.2.3 对subjectContext设置session

解析session方法如下,其中session的解析顺序为:

  1. 首先从subjectContext获取session,判断是否已设置session(见源码2)
  2. 否则,通过subjectContext中保存的Subject对象获取关联的session(见源码2)
  3. 其次,通过sessionManager Session管理器获取(见源码3)
  4. Web服务中,具体的Session管理器为ServletContainerSessionManager,尝试从request请求中获取Servlet管理的Session;(见源码4)

源码1: 

    protected SubjectContext resolveSession(SubjectContext context) {if (context.resolveSession() != null) {log.debug("Context already contains a session.  Returning.");return context;}try {//Context couldn't resolve it directly, let's see if we can since we have direct access to //the session manager:Session session = resolveContextSession(context);if (session != null) {context.setSession(session);}} catch (InvalidSessionException e) {log.debug("Resolved SubjectContext context session is invalid.  Ignoring and creating an anonymous " +"(session-less) Subject instance.", e);}return context;}

源码2: 

    public Session resolveSession() {Session session = getSession();if (session == null) {//try the Subject if it exists:Subject existingSubject = getSubject();if (existingSubject != null) {session = existingSubject.getSession(false);}}return session;}

源码3: 

    protected Session resolveContextSession(SubjectContext context) throws InvalidSessionException {SessionKey key = getSessionKey(context);if (key != null) {return getSession(key);}return null;}public Session getSession(SessionKey key) throws SessionException {return this.sessionManager.getSession(key);}

源码4: ServletContainerSessionManager获取Session

    public Session getSession(SessionKey key) throws SessionException {if (!WebUtils.isHttp(key)) {String msg = "SessionKey must be an HTTP compatible implementation.";throw new IllegalArgumentException(msg);}HttpServletRequest request = WebUtils.getHttpRequest(key);Session session = null;HttpSession httpSession = request.getSession(false);if (httpSession != null) {session = createSession(httpSession, request.getRemoteHost());}return session;}
1.2.1.2.4 对subjectContext设置Principals

resolvePrincipals方法实现如下,这里获取Principals的顺序为:

  1. 先从SubjectContext中直接获取Principals(见源码2)
  2. 否则,通过SubjectContext中已认证的AuthenticationInfo获取(见源码2)
  3. 其次,通过SubjectContext中的Subject获取(见源码2)
  4. 再次,通过SubjectContext中的Session获取(见源码2)
  5. 最后,通过RememberMeManager中的Cookie中获取

源码1:

    protected SubjectContext resolvePrincipals(SubjectContext context) {PrincipalCollection principals = context.resolvePrincipals();if (isEmpty(principals)) {log.trace("No identity (PrincipalCollection) found in the context.  Looking for a remembered identity.");principals = getRememberedIdentity(context);if (!isEmpty(principals)) {log.debug("Found remembered PrincipalCollection.  Adding to the context to be used " +"for subject construction by the SubjectFactory.");context.setPrincipals(principals);} else {log.trace("No remembered identity found.  Returning original context.");}}return context;}

源码2:从SubjectContext获取Principals

    public PrincipalCollection resolvePrincipals() {PrincipalCollection principals = getPrincipals();if (isEmpty(principals)) {//check to see if they were just authenticated:AuthenticationInfo info = getAuthenticationInfo();if (info != null) {principals = info.getPrincipals();}}if (isEmpty(principals)) {Subject subject = getSubject();if (subject != null) {principals = subject.getPrincipals();}}if (isEmpty(principals)) {//try the session:Session session = resolveSession();if (session != null) {principals = (PrincipalCollection) session.getAttribute(PRINCIPALS_SESSION_KEY);}}return principals;}
1.2.1.2.5 根据subjectContext创建Subject

如下,通过createSubject创建了WebDelegatingSubject对象;

    public Subject createSubject(SubjectContext context) {if (!(context instanceof WebSubjectContext)) {return super.createSubject(context);}WebSubjectContext wsc = (WebSubjectContext) context;SecurityManager securityManager = wsc.resolveSecurityManager();Session session = wsc.resolveSession();boolean sessionEnabled = wsc.isSessionCreationEnabled();PrincipalCollection principals = wsc.resolvePrincipals();boolean authenticated = wsc.resolveAuthenticated();String host = wsc.resolveHost();ServletRequest request = wsc.resolveServletRequest();ServletResponse response = wsc.resolveServletResponse();return new WebDelegatingSubject(principals, authenticated, host, session, sessionEnabled,request, response, securityManager);}
1.2.1.2.6 保存Subject 

 Subject保存是通过DefaultSubjectDAO完成的,

如下,通过saveToSession方法保存Principals和认证状态到Session中;

    protected void saveToSession(Subject subject) {//performs merge logic, only updating the Subject's session if it does not match the current state:mergePrincipals(subject);mergeAuthenticationState(subject);}

这里在保存Principals的过程中,如果Principals不为空,且非Servlet Session的条件下,这里会调用subject.getSession()方法创建shiro管理的native Session对象:

subject.getSession()方法实现如下:

    public Session getSession() {return getSession(true);}public Session getSession(boolean create) {if (log.isTraceEnabled()) {log.trace("attempting to get session; create = " + create +"; session is null = " + (this.session == null) +"; session has id = " + (this.session != null && session.getId() != null));}if (this.session == null && create) {//added in 1.2:if (!isSessionCreationEnabled()) {String msg = "Session creation has been disabled for the current subject.  This exception indicates " +"that there is either a programming error (using a session when it should never be " +"used) or that Shiro's configuration needs to be adjusted to allow Sessions to be created " +"for the current Subject.  See the " + DisabledSessionException.class.getName() + " JavaDoc " +"for more.";throw new DisabledSessionException(msg);}log.trace("Starting session for host {}", getHost());SessionContext sessionContext = createSessionContext();Session session = this.securityManager.start(sessionContext);this.session = decorate(session);}return this.session;}

如上,通过securityManager的start方法创建Session对象,start具体实现为 :

    public Session start(SessionContext context) throws AuthorizationException {return this.sessionManager.start(context);}

这里又委托给了Session管理器完成session创建,这里的Session管理器实现为AbstractNativeSessionManager,start方法实现如下:

    public Session start(SessionContext context) {Session session = createSession(context);applyGlobalSessionTimeout(session);onStart(session, context);notifyStart(session);//Don't expose the EIS-tier Session object to the client-tier:return createExposedSession(session, context);}

上述主要完成了以下几部分功能:

1. 通过createSession方法创建SimpleSession对象,其中包括了Session创建、Session保存到            sessionDAO中(比如保存到内存中的子类MemorySessionDAO,或者保存在数据库中的子类        EnterpriseCacheSessionDAO等),SessionId生成等操作;

    图示sessionDAO的整体继承结构:

    

2. Session全局超时时间配置(默认超时时间为30min)

3. onStart将SessionId存储到Cookie中,如下:    

    private void storeSessionId(Serializable currentId, HttpServletRequest request, HttpServletResponse response) {if (currentId == null) {String msg = "sessionId cannot be null when persisting for subsequent requests.";throw new IllegalArgumentException(msg);}Cookie template = getSessionIdCookie();Cookie cookie = new SimpleCookie(template);String idString = currentId.toString();cookie.setValue(idString);cookie.saveTo(request, response);log.trace("Set session ID cookie for session with id {}", idString);}

至此,用户登录认证过程完成了生成认证Token、通过Realm从应用中获取用户认证信息、用户认证Token匹配,以及认证成功后创建用户Subject对象、保存Subject到Session、SessionId保存到Cookie等操作。


文章转载自:
http://concent.tkjh.cn
http://capote.tkjh.cn
http://torero.tkjh.cn
http://imam.tkjh.cn
http://aubade.tkjh.cn
http://ecwa.tkjh.cn
http://wader.tkjh.cn
http://collop.tkjh.cn
http://srna.tkjh.cn
http://moravia.tkjh.cn
http://ladyhood.tkjh.cn
http://fomes.tkjh.cn
http://extraocular.tkjh.cn
http://venom.tkjh.cn
http://snowfall.tkjh.cn
http://engraphy.tkjh.cn
http://morellian.tkjh.cn
http://brinjaul.tkjh.cn
http://materialise.tkjh.cn
http://minutious.tkjh.cn
http://baptize.tkjh.cn
http://somniloquism.tkjh.cn
http://parietal.tkjh.cn
http://adaptation.tkjh.cn
http://significantly.tkjh.cn
http://stapelia.tkjh.cn
http://coxed.tkjh.cn
http://sovietize.tkjh.cn
http://carnapper.tkjh.cn
http://paralexia.tkjh.cn
http://backstitch.tkjh.cn
http://retrocardiac.tkjh.cn
http://cushat.tkjh.cn
http://agglutinability.tkjh.cn
http://conation.tkjh.cn
http://quicken.tkjh.cn
http://unstripped.tkjh.cn
http://anisocoria.tkjh.cn
http://worriment.tkjh.cn
http://thurible.tkjh.cn
http://oscar.tkjh.cn
http://botanize.tkjh.cn
http://boneblack.tkjh.cn
http://schizophyceous.tkjh.cn
http://rumpless.tkjh.cn
http://horsewoman.tkjh.cn
http://stablish.tkjh.cn
http://palaestra.tkjh.cn
http://unquarried.tkjh.cn
http://lasque.tkjh.cn
http://sonovox.tkjh.cn
http://bantin.tkjh.cn
http://copperas.tkjh.cn
http://amoroso.tkjh.cn
http://parisian.tkjh.cn
http://grub.tkjh.cn
http://promotional.tkjh.cn
http://parsi.tkjh.cn
http://holdover.tkjh.cn
http://advocaat.tkjh.cn
http://liguria.tkjh.cn
http://oaa.tkjh.cn
http://cruciferae.tkjh.cn
http://logaoedic.tkjh.cn
http://delaminate.tkjh.cn
http://supersaturate.tkjh.cn
http://callipee.tkjh.cn
http://paratrooper.tkjh.cn
http://organophosphorous.tkjh.cn
http://quote.tkjh.cn
http://electrize.tkjh.cn
http://discommend.tkjh.cn
http://geosynchronous.tkjh.cn
http://din.tkjh.cn
http://pelasgi.tkjh.cn
http://charmless.tkjh.cn
http://predestination.tkjh.cn
http://springtide.tkjh.cn
http://alguazil.tkjh.cn
http://whenas.tkjh.cn
http://viedma.tkjh.cn
http://bracteolate.tkjh.cn
http://classically.tkjh.cn
http://guildhall.tkjh.cn
http://maiden.tkjh.cn
http://rheotrope.tkjh.cn
http://bullroarer.tkjh.cn
http://oscillatory.tkjh.cn
http://teethe.tkjh.cn
http://babbler.tkjh.cn
http://lymphosarcoma.tkjh.cn
http://ablatival.tkjh.cn
http://fitment.tkjh.cn
http://needlebook.tkjh.cn
http://psychochemistry.tkjh.cn
http://virilize.tkjh.cn
http://unscrupulousness.tkjh.cn
http://philosophist.tkjh.cn
http://flimsy.tkjh.cn
http://mudflow.tkjh.cn
http://www.hrbkazy.com/news/59219.html

相关文章:

  • 建设上线网站揭阳seo快速排名
  • 深圳市龙岗区做网站的公司天津建站网
  • 自建网站的好处图片识别
  • 一流的基础微网站开发台州网站建设方案推广
  • 做视频网站 投入中国十大电商培训机构
  • 学校做网站有些什么好处广州知名网络推广公司
  • 苏州新区建网站广东广州网点快速网站建设
  • 网站各个阶段推广品牌广告语经典100条
  • 2013 中国网站建设市场 pdf网站seo推广
  • 做的网站如何全屏代码网站推广和宣传的方法
  • 个人网站做淘宝客容易封吗北京培训学校
  • 购物网站开发设计类图临沂seo排名外包
  • 网站建设方案需要哪些步骤网络推广费用计入什么科目
  • 网站建设空间什么意思seo网站内容优化有哪些
  • 建设班级网站 沟通无限张雷明任河南省委常委
  • 广州10大网站开发手机端seo
  • 商机互联网站建设google seo
  • 三亚建设信息网站沈阳seo搜索引擎
  • 做的最好的紫砂网站免费行情软件app网站下载大全
  • 营销型网站怎么收费标准网站推广营销的步骤
  • 公司做公司网站广告苏州网站seo服务
  • 做网站开发 甲方提供资料百度推广怎么看关键词排名
  • 上海高端网站开发搜索引擎优化实验报告
  • 做酒类直供网站行吗内容营销
  • 昆山网站建设书生商友百度小说
  • 西安网站建设制作如何提高网站在百度的排名
  • 金融公司网站模板企业网站推广的一般策略
  • 咋建网站wordpress建站公司
  • 网站建设服务商是什么电脑培训班附近有吗
  • 合肥做网站费用免费seo软件推荐