为什么下不了王者荣耀官网。

springboot + shiro + cas4.2.7 实战
时间: 01:10:03
&&&& 阅读:498
&&&& 评论:
&&&& 收藏:0
标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&1. 下载地址&/apereo/cas/archive/v4.2.7.zip
2. 解压后, 用intellj idea 打开
3. 执行 gradle build -x test ,打包编译
4. 取消https,能够支持直接http
cas.properties 修改两个地方
# Decides whether SSO cookie should be created only under secure connections.
tgc.secure=false
# The expiration value of the SSO cookie
# tgc.maxAge=-1
# The name of the SSO cookie
# tgc.name=TGC
# The path to which the SSO cookie will be scoped
# tgc.path=/cas
# The expiration value of the SSO cookie for long-term authentications
# tgc.remember.me.maxAge=1209600
# Decides whether SSO Warning cookie should be created only under secure connections.
warn.cookie.secure=false
casLoginView.jsp
&jsp:directive.include file="includes/top.jsp" /&
&%--&c:if test="${not pageContext.request.secure}"&
&div id="msg" class="errors"&
&h2&&spring:message code="screen.nonsecure.title" /&&/h2&
&p&&spring:message code="screen.nonsecure.message" /&&/p&
&/c:if&--%&
注销上面的代码
HTTPSandIMAPS-.json 中增加http的service
"@class" : "org.jasig.cas.services.RegexRegisteredService",
"serviceId" : "^(https|imaps|http)://.*",
"name" : "HTTPS and IMAPS",
"description" : "This service definition authorized all application urls that support HTTPS and IMAPS protocols.",
"proxyPolicy" : {
"@class" : "org.jasig.cas.services.RefuseRegisteredServiceProxyPolicy"
"evaluationOrder" : 10000,
"usernameAttributeProvider" : {
"@class" : "org.jasig.cas.services.DefaultRegisteredServiceUsernameProvider"
"logoutType" : "BACK_CHANNEL",
"attributeReleasePolicy" : {
"@class" : "org.jasig.cas.services.ReturnAllowedAttributeReleasePolicy",
"principalAttributesRepository" : {
"@class" : "org.jasig.cas.authentication.principal.DefaultPrincipalAttributesRepository"
"authorizedToReleaseCredentialPassword" : false,
"authorizedToReleaseProxyGrantingTicket" : false
"accessStrategy" : {
"@class" : "org.jasig.cas.services.DefaultRegisteredServiceAccessStrategy",
"enabled" : true,
"ssoEnabled" : true
5. 修改cas的认证方式,包括jdbc、 自定义密码加密等
5.1 修改&cas-server-webapp 的依赖,修改 该工程下build.gradle
5.2 修改cas.properties
cas.jdbc.authn.query.sql=SELECT username,pwd,salt,`status` FROM sys_user WHERE username = ?
cas.jdbc.authn.search.password=root
cas.jdbc.authn.search.user=root
5.3 自定义密码加密&CustomEncoder
package org.jasig.cas.adaptors.
import org.apache.shiro.crypto.RandomNumberG
import org.apache.shiro.crypto.SecureRandomNumberG
import org.apache.shiro.crypto.hash.SimpleH
import org.apache.shiro.util.ByteS
import org.springframework.beans.factory.annotation.V
* Created by 肖建锋 on .
public class CustomEncoder {
/** 随机字符生产工具 */
private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
/** 加密方式 */
private String algorithmName = "sha";
/** 多重加密次数 */
private int hashIterations = 2;
* 配置随机字符生产工具
* @param randomNumberGenerator
* @author xiaojf
public void setRandomNumberGenerator(RandomNumberGenerator randomNumberGenerator) {
this.randomNumberGenerator = randomNumberG
* 配置加密方式
* @param algorithmName
* @author xiaojf
public void setAlgorithmName(String algorithmName) {
this.algorithmName = algorithmN
* 配置重复加密次数
* @param hashIterations
* @author xiaojf
public void setHashIterations(int hashIterations) {
this.hashIterations = hashI
* 密码加密
@param pwd
@param salt
* @author xiaojf
public String encryptPassword(String pwd,String salt) {
return new SimpleHash(algorithmName, pwd, ByteSource.Util.bytes(salt), hashIterations).toHex();
* 根据私钥加密
* @param value 要加密字段
* @param salt 密钥
* @author hp
* @return 加密后字段
public String encrypt(String value , String salt) {
new SimpleHash(algorithmName, value, ByteSource.Util.bytes(salt), hashIterations).toHex();
* 获取加密后的新密码
* @param pwd 密码
* @param salt 盐
* @return 新密码
* @author xiaojf
public String getEncryptPassword(String pwd,String salt){
String newPassword = new SimpleHash(algorithmName, pwd, ByteSource.Util.bytes(salt), hashIterations).toHex();
return newP
* @param args the input arguments
* @author xiaojf
public static void main(String[] args) {
/*SysUser sysUser = new SysUser();
sysUser.setUsername("super");
sysUser.setPwd("admin");*/
String s = new CustomEncoder().encrypt("super","e910c85b7f5c5e789d50fafcfa5d4efc");
System.out.println(s);
5.4 自定义用户认证方式
package org.jasig.cas.adaptors.
import mons.lang3.StringU
import org.jasig.cas.authentication.HandlerR
import org.jasig.cas.authentication.PreventedE
import org.jasig.cas.authentication.UsernamePasswordC
import org.springframework.beans.factory.annotation.A
import org.springframework.beans.factory.annotation.Q
import org.springframework.beans.factory.annotation.V
import org.springframework.dao.DataAccessE
import org.springframework.dao.IncorrectResultSizeDataAccessE
import org.springframework.jdbc.support.rowset.SqlRowS
import org.
import javax.security.auth.login.AccountNotFoundE
import javax.security.auth.login.FailedLoginE
import javax.sql.DataS
import javax.validation.constraints.NotN
import java.security.GeneralSecurityE
import java.util.M
* Class that if provided a query that returns a password (parameter of query
* must be username) will compare that password to a translated version of the
* password provided by the user. If they match, then authentication succeeds.
* Default password translator is plaintext translator.
* @author Scott Battaglia
* @author Dmitriy Kopylenko
* @author Marvin S. Addison
* @since 3.0.0
@Component("queryDatabaseAuthenticationHandler2")
public class QueryDatabaseAuthenticationHandler2 extends AbstractJdbcUsernamePasswordAuthenticationHandler {
protected final HandlerResult authenticateUsernamePasswordInternal(final UsernamePasswordCredential credential)
throws GeneralSecurityException, PreventedException {
if (StringUtils.isBlank(this.sql) || getJdbcTemplate() == null) {
throw new GeneralSecurityException("Authentication handler is not configured correctly");
final String username = credential.getUsername();
Map&String, Object& map = getJdbcTemplate().queryForMap(sql, username);
final String dbPassword
= map.get("pwd")+"";
String salt = map.get("salt")+"";
int status = Integer.parseInt(map.get("status")+"");
final String encryptedPassword = new CustomEncoder().encryptPassword(credential.getPassword(),salt);
if (!dbPassword.equals(encryptedPassword)) {
throw new FailedLoginException("Password does not match value on record.");
} catch (final IncorrectResultSizeDataAccessException e) {
if (e.getActualSize() == 0) {
throw new AccountNotFoundException(username + " not found with SQL query");
throw new FailedLoginException("Multiple records found for " + username);
} catch (final DataAccessException e) {
throw new PreventedException("SQL exception while executing query for " + username, e);
return createHandlerResult(credential, this.principalFactory.createPrincipal(username), null);
* @param sql The sql to set.
@Autowired
public void setSql(@Value("${cas.jdbc.authn.query.sql:}") final String sql) {
this.sql =
@Autowired(required = false)
public void setDataSource(@Qualifier("queryDatabaseDataSource") final DataSource dataSource) {
super.setDataSource(dataSource);
6. 自定义数据源
&?xml version="1.0" encoding="UTF-8"?&
&beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"&
&!-- 设置druid数据源 --&
&bean id="queryDatabaseDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"&
&property name="url" value="jdbc:mysql://localhost:3306/tomorrow?characterEncoding=utf-8" /&
&property name="username" value="root" /&
&property name="password" value="root" /&
&property name="driverClassName" value="com.mysql.jdbc.Driver"&&/property&
7. 修改 cas默认的用户校验方式,注入我们上面定义的规则&deployerConfigContext.xml
&?xml version="1.0" encoding="UTF-8"?&
&beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"&
&util:map id="authenticationHandlersResolvers"&
&entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" /&
&entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" /&
&/util:map&
&util:list id="authenticationMetadataPopulators"&
&ref bean="successfulHandlerMetaDataPopulator" /&
&ref bean="rememberMeAuthenticationMetaDataPopulator" /&
&/util:list&
&bean id="attributeRepository" class="org.jasig.services.persondir.support.NamedStubPersonAttributeDao"
p:backingMap-ref="attrRepoBackingMap" /&
&alias name="queryDatabaseAuthenticationHandler2" alias="primaryAuthenticationHandler" /&
&alias name="personDirectoryPrincipalResolver" alias="primaryPrincipalResolver" /&
&util:map id="attrRepoBackingMap"&
&entry key="uid" value="uid" /&
&entry key="eduPersonAffiliation" value="eduPersonAffiliation" /&
&entry key="groupMembership" value="groupMembership" /&
&key&&value&memberOf&/value&&/key&
&value&faculty&/value&
&value&staff&/value&
&value&org&/value&
&/util:map&
&alias name="serviceThemeResolver" alias="themeResolver" /&
&alias name="jsonServiceRegistryDao" alias="serviceRegistryDao" /&
&alias name="defaultTicketRegistry" alias="ticketRegistry" /&
&alias name="ticketGrantingTicketExpirationPolicy" alias="grantingTicketExpirationPolicy" /&
&alias name="multiTimeUseOrTimeoutExpirationPolicy" alias="serviceTicketExpirationPolicy" /&
&alias name="anyAuthenticationPolicy" alias="authenticationPolicy" /&
&alias name="acceptAnyAuthenticationPolicyFactory" alias="authenticationPolicyFactory" /&
&bean id="auditTrailManager"
class="org.jasig.inspektr.audit.support.Slf4jLoggingAuditTrailManager"
p:entrySeparator="${cas.audit.singleline.separator:|}"
p:useSingleLine="${cas.audit.singleline:false}"/&
&alias name="neverThrottle" alias="authenticationThrottle" /&
&util:list id="monitorsList"&
&ref bean="memoryMonitor" /&
&ref bean="sessionMonitor" /&
&/util:list&
&alias name="defaultPrincipalFactory" alias="principalFactory" /&
&alias name="defaultAuthenticationTransactionManager" alias="authenticationTransactionManager" /&
&alias name="defaultPrincipalElectionStrategy" alias="principalElectionStrategy" /&
&alias name="tgcCipherExecutor" alias="defaultCookieCipherExecutor" /&
&8. 打war包,复制到tomcat下启动,或者直接用idea运行
&到这里cas安装修改完成了,接下来是修改springboot+shiro + cas(重点部分,已经用红色标出)
9. 加入shiro-cas依赖
compile ("org.apache.shiro:shiro-cas:1.3.2")
10. 修改 ShiroConfiguration.java&自定义cas filter
public CasFilter casFilter() {
CasFilter casFilter = new CasFilter();
casFilter.setFailureUrl("/error/403");
casFilter.setSuccessUrl("/");
return casF
11. 自定义 cas realm
public MyCasRealm myCasRealm(RetryLimitHashedCredentialsMatcher credentialsMatcher) {
MyCasRealm casRealm = new MyCasRealm();
casRealm.setCachingEnabled(true);
casRealm.setCasServerUrlPrefix("http://localhost:8080/cas");
casRealm.setCasService("http://localhost:8000/shiro-cas");
12. 自定义多点登出逻辑,不能使用shiro自带的logout filter
package cn.xiaojf.today.sys.web.
import cn.xiaojf.today.base.constant.SystemC
import cn.xiaojf.today.monR
import cn.xiaojf.today.base.exception.BusinessE
import cn.xiaojf.today.base.web.controller.BaseC
import cn.xiaojf.today.log.OperationT
import cn.xiaojf.today.log.annotation.OperateL
import cn.xiaojf.today.sys.entity.SysU
import cn.xiaojf.today.sys.service.SysUserS
import mons.lang3.StringU
import org.apache.shiro.SecurityU
import org.apache.shiro.authc.AuthenticationE
import org.apache.shiro.authc.UsernamePasswordT
import org.apache.shiro.subject.S
import org.apache.shiro.web.session.HttpServletS
import org.slf4j.L
import org.slf4j.LoggerF
import org.springframework.beans.factory.annotation.A
import org.springframework.web.bind.annotation.RequestM
import org.springframework.web.bind.annotation.RestC
import org.springframework.web.servlet.ModelAndV
import javax.servlet.http.HttpS
import java.util.S
* @author xiaojf
@RestController
@RequestMapping("/login")
public class LoginController {
@Autowired
private SysUserService userS
Logger logger = LoggerFactory.getLogger(LoginController.class);
@RequestMapping("index")
public ModelAndView index() {
ModelAndView mv = new ModelAndView("/login");
@RequestMapping("out")
public ModelAndView out() {
SecurityUtils.getSubject().logout();
ModelAndView view = new ModelAndView();
view.setViewName("redirect:http://localhost:8080/cas/logout");
@RequestMapping("auth")
@OperateLog(module = OperationType.LOGIN)
public ModelAndView auth(String username, String password, boolean rememberMe, HttpSession session) {
ModelAndView mv = new ModelAndView("/");
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);
subject.login(token);
SysUser user = userService.getByUsername(username);
//在session中保存当前用户的个人信息
session.setAttribute(SystemConstant.SYS_CURRENT_USER, user);
//在session中保存用户的r_path
Set&String& rpathSet = userService.loadUserRPath(user.getId());
String rpath = StringUtils.join(rpathSet, SystemConstant.DATA_SPLIT_CHAR);
session.setAttribute(SystemConstant.SYS_CURRENT_USER_RPATH, rpath);
} catch (IllegalArgumentException e) {
mv.setViewName("/login/index");
mv.addObject("errMsg","参数异常");
logger.error(e.getMessage(), e.getStackTrace());
} catch (AuthenticationException e) {
logger.error(e.getMessage(), e.getStackTrace());
mv.setViewName("/login/index");
mv.addObject("errMsg","认证失败");
} catch (BusinessException e) {
mv.setViewName("/login/index");
mv.addObject("errMsg","登录异常");
logger.error(e.getMessage(), e.getStackTrace());
} catch (Exception e) {
mv.setViewName("/login/index");
mv.addObject("errMsg","登录异常");
logger.error(e.getMessage(), e.getStackTrace());
13. 完整的&ShiroConfiguration
package cn.xiaojf.today.shiro.
import at.pollux.thymeleaf.shiro.dialect.ShiroD
import cn.xiaojf.today.sys.security.credentials.RetryLimitHashedCredentialsM
import cn.xiaojf.today.sys.security.filter.RoleAuthorizationF
import cn.xiaojf.today.sys.security.realm.MyCasR
import cn.xiaojf.today.sys.security.realm.UsernameR
import cn.xiaojf.today.sys.service.SysResS
import org.apache.shiro.cas.CasF
import org.apache.shiro.spring.LifecycleBeanPostP
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceA
import org.apache.shiro.spring.web.ShiroFilterFactoryB
import org.apache.shiro.web.filter.authc.LogoutF
import org.apache.shiro.web.mgt.DefaultWebSecurityM
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyC
import org.springframework.boot.web.servlet.FilterRegistrationB
import org.springframework.context.ApplicationC
import org.springframework.context.annotation.B
import org.springframework.context.annotation.C
import org.springframework.web.filter.DelegatingFilterP
import javax.servlet.F
import java.util.LinkedHashM
import java.util.M
* shiro配置
* @author xiaojf
@Configuration
public class ShiroConfiguration {
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new DelegatingFilterProxy("shiroFilter"));
filterRegistrationBean.addInitParameter("targetFilterLifecycle", "true");
filterRegistrationBean.setEnabled(true);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationB
public RetryLimitHashedCredentialsMatcher credentialsMatcher() {
RetryLimitHashedCredentialsMatcher credentialsMatcher = new RetryLimitHashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("sha");
credentialsMatcher.setHashIterations(2);
credentialsMatcher.setStoredCredentialsHexEncoded(true);
credentialsMatcher.setRetryCount(5);
credentialsMatcher.setRetryTime(1800000);
return credentialsM
public UsernameRealm usernameRealm(RetryLimitHashedCredentialsMatcher credentialsMatcher) {
UsernameRealm usernameRealm = new UsernameRealm();
usernameRealm.setCredentialsMatcher(credentialsMatcher);
usernameRealm.setCachingEnabled(true);
public MyCasRealm myCasRealm(RetryLimitHashedCredentialsMatcher credentialsMatcher) {
MyCasRealm casRealm = new MyCasRealm();
casRealm.setCredentialsMatcher(credentialsMatcher);
casRealm.setCachingEnabled(true);
casRealm.setCasServerUrlPrefix("http://localhost:8080/cas");
casRealm.setCasService("http://localhost:8000/shiro-cas");
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
daap.setProxyTargetClass(true);
public DefaultWebSecurityManager securityManager(UsernameRealm usernameRealm) {
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
dwsm.setRealm(usernameRealm);
public DefaultWebSecurityManager securityManager(MyCasRealm myCasRealm) {
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
dwsm.setRealm(myCasRealm);
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager defaultWebSecurityManager) {
AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
aasa.setSecurityManager(defaultWebSecurityManager);
public CasFilter casFilter() {
CasFilter casFilter = new CasFilter();
casFilter.setFailureUrl("/error/403");
casFilter.setSuccessUrl("/");
return casF
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager, CasFilter casFilter,ApplicationContext context) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("http://localhost:8080/cas/login?service=http://localhost:8000/shiro-cas");
shiroFilterFactoryBean.setUnauthorizedUrl("http://localhost:8080/cas/login?service=http://localhost:8000/shiro-cas");
Map&String, Filter& filters = new LinkedHashMap&&();
filters.put("role", new RoleAuthorizationFilter());
filters.put("cas",casFilter);
shiroFilterFactoryBean.getFilters().putAll(filters);
SysResService resService = context.getBean(SysResService.class);
loadShiroFilterChain(shiroFilterFactoryBean,resService);
return shiroFilterFactoryB
private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean,SysResService resService) {
Map&String, String& filterChainDefinitionMap = new LinkedHashMap&&();
filterChainDefinitionMap.put("/login/index", "anon");
filterChainDefinitionMap.put("/error/403", "anon");
filterChainDefinitionMap.put("/error/404", "anon");
filterChainDefinitionMap.put("/error/500", "anon");
filterChainDefinitionMap.put("/login/auth", "anon");
filterChainDefinitionMap.put("login/out", "authc");
filterChainDefinitionMap.put("/plugins/**", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/images/**", "anon");
filterChainDefinitionMap.put("/shiro-cas", "cas");
filterChainDefinitionMap = resService.loadFilterChainDefinitions(filterChainDefinitionMap);
filterChainDefinitionMap.put("/**", "role[ROLE_SUPER]");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
public ShiroDialect shiroDialect() {
return new ShiroDialect();
14. 修改完成以后, 直接访问自己的项目会自动的跳转到cas的登陆页面,登陆完成后会跳转到自己项目的首页,注意主要是下面这句话的效果
shiroFilterFactoryBean.setLoginUrl("http://localhost:8080/cas/login?service=http://localhost:8000/shiro-cas");首先登陆的请求被转发到
http://localhost:8080/cas/login然后登陆成功会返回ticket,并重定向到
http://localhost:8000/shiro-casshiro-cas 会通过cas filter 拦截到shiro-cas,然后根据返回的username 对用户进行授权还有一点需要注意的时候 , cas 默认接受的参数名称是
username, password ,可以在cas.properties 中修改默认, 下面是我的登陆表单代码,仅供参考
&form action="/login/auth" method="post"&
&input id="username" type="text" name="username" class="username" placeholder="用户名"&
&input id="pwd" type="password" name="pwd" class="password" placeholder="密码"&
&button type="submit"&登 录&/button&
&标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&国之画&&&& &&&&chrome插件
版权所有 京ICP备号-2
迷上了代码!}

我要回帖

更多关于 王者荣耀官网 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信