SpringSecurity如何验证accesstoken
在客户端获取到令牌后,访问资源时,可以设置如下请求头或者请求参数中加上 access_token。Authorization: Bearer access_token
在前两篇《auth2生成token》和《token转jwt》中,我们已经知道 access_token 产生的大体流程,并且知道如何自定义 token 的格式,其中,授权服务器 配置中的 AuthorizationServerEndpointsConfigurer 可谓极为重要;而关于access_token 的验证流程,我们就从对应的资源服务器 的配置入手。1 资源服务器配置
与生成access_token 有固定接口 /oauth/token 不同,我们肯定不能通过业务资源的接口地址去找到验证 token 源码的入口。SpringSecurity原理是责任链模式,绝大数功能都是过滤器实现的,这里也不例外。资源服务器的配置如下:
这里重点关注一下ResourceServerSecurityConfigurer。@Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources .resourceId("message")//设置资源id .tokenStore(new JwtTokenStore(jwtAccessTokenConverter())) .tokenServices(new RemoteTokenServices()) .tokenExtractor(new BearerTokenExtractor()) ; } @Override public void configure(HttpSecurity http) throws Exception { super.configure(http); } }2 ResourceServerSecurityConfigurer
在 ResourceServerSecurityConfigurer 中我们看到鉴权 所需要的两个重要元素,Filter 和 AuthenticationManager。看到下面的代码,一些比较敏感的小伙伴可能会推测,所谓的 access_token 验证应该就是用这里配置的 Filter 和 AuthenticationManager进行鉴权了。public final class ResourceServerSecurityConfigurer extends SecurityConfigurerAdapter { ...... private OAuth2AuthenticationProcessingFilter resourcesServerFilter; private AuthenticationManager authenticationManager; ...... @Override public void configure(HttpSecurity http) throws Exception { AuthenticationManager oauthAuthenticationManager = oauthAuthenticationManager(http); //N 指定过滤器 resourcesServerFilter = new OAuth2AuthenticationProcessingFilter(); resourcesServerFilter.setAuthenticationEntryPoint(authenticationEntryPoint); //N 设置 AuthenticationManager resourcesServerFilter.setAuthenticationManager(oauthAuthenticationManager); if (eventPublisher != null) { resourcesServerFilter.setAuthenticationEventPublisher(eventPublisher); } if (tokenExtractor != null) { resourcesServerFilter.setTokenExtractor(tokenExtractor); } resourcesServerFilter = postProcess(resourcesServerFilter); resourcesServerFilter.setStateless(stateless); // @formatter:off http .authorizeRequests().expressionHandler(expressionHandler) .and() //N 将过滤器放到过滤器链中 .addFilterBefore(resourcesServerFilter, AbstractPreAuthenticatedProcessingFilter.class) .exceptionHandling() .accessDeniedHandler(accessDeniedHandler) .authenticationEntryPoint(authenticationEntryPoint); // @formatter:on } private AuthenticationManager oauthAuthenticationManager(HttpSecurity http) { OAuth2AuthenticationManager oauthAuthenticationManager = new OAuth2AuthenticationManager(); if (this.authenticationManager != null) { //如果自己配置了 authenticationManager if (authenticationManager instanceof OAuth2AuthenticationManager) { oauthAuthenticationManager = (OAuth2AuthenticationManager) authenticationManager; } else { return authenticationManager; } } oauthAuthenticationManager.setResourceId(resourceId); oauthAuthenticationManager.setTokenServices(resourceTokenServices(http)); oauthAuthenticationManager.setClientDetailsService(clientDetails()); return oauthAuthenticationManager; } ...... }
通过上面的代码.addFilterBefore(resourcesServerFilter, AbstractPreAuthenticatedProcessingFilter.class)我们可以知道,用于处理令牌验证的过滤器应该就是 OAuth2AuthenticationProcessingFilter 了,并且默认AuthenticationManager是OAuth2AuthenticationManager。3 OAuth2AuthenticationProcessingFilter
过滤器肯定是要看 doFilter 了,核心逻辑如下:从request中提取出 access_token,并构建 authentication 对象;设置 authentication 对象的 details (token_type、token_value、sessionId、remoteAddress 等,来源于request);利用 authentication 对象进行鉴权。鉴权成功对象放到 SecurityContext 中public class OAuth2AuthenticationProcessingFilter implements Filter, InitializingBean { private TokenExtractor tokenExtractor = new BearerTokenExtractor(); ...... public void setAuthenticationManager(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; } public void setTokenExtractor(TokenExtractor tokenExtractor) { this.tokenExtractor = tokenExtractor; } ...... public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { boolean debug = logger.isDebugEnabled(); HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { //N 从 request 中提取access_token Authentication authentication = this.tokenExtractor.extract(request); if (authentication == null) { if (this.stateless && this.isAuthenticated()) { if (debug) { logger.debug("Clearing security context."); } SecurityContextHolder.clearContext(); } if (debug) { logger.debug("No token in request, will continue chain."); } } else { //N 把access_token 放到request属性中 {"OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE":"access_token"} request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal()); if (authentication instanceof AbstractAuthenticationToken) { AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication; //request中的 remoteAddress、sessionId、tokenValue、tokenType 等信息放到details中 needsDetails.setDetails(this.authenticationDetailsSource.buildDetails(request)); } //N 鉴权 Authentication authResult = this.authenticationManager.authenticate(authentication); if (debug) { logger.debug("Authentication success: " + authResult); } this.eventPublisher.publishAuthenticationSuccess(authResult); //N 设置安全上下文 SecurityContextHolder.getContext().setAuthentication(authResult); } } catch (OAuth2Exception e) { SecurityContextHolder.clearContext(); if (debug) { logger.debug("Authentication request failed: " + e); } this.eventPublisher.publishAuthenticationFailure(new BadCredentialsException(e.getMessage(), e), new PreAuthenticatedAuthenticationToken("access-token", "N/A")); this.authenticationEntryPoint.commence(request, response, new InsufficientAuthenticationException(e.getMessage(), e)); return; } chain.doFilter(request, response); } ...... }
关于 BearerTokenExtractor,作用就是从request 的请求头或者请求参数中提取access_token,并设置 tokenType(例如bearer类型),逻辑相对简单,这里就不展开了。当然我们也可以定义自己的 token 提取器,只要在配置资源服务器时设置给 ResourceServerSecurityConfigurer 对象即可。4 OAuth2AuthenticationManager
AuthenticationManager 的作用就是鉴权,这里的逻辑也很简单,就是验证token是否是真的由授权服务器产生,如果是,继续校验resourceId 和scope。资源服务器默认的resourceId 在 ResourceServerSecurityConfigurer 类中,是 "oauth2-resource"。
如果令牌对应的 resourceIds 是空的,就不校验resourceId了,换种说法就是:如果我们数据库接入端表中没配置resourceId,就拥有所有资源服务器的访问权限,总感觉不爽。public class OAuth2AuthenticationManager implements AuthenticationManager, InitializingBean { private ResourceServerTokenServices tokenServices; ...... public Authentication authenticate(Authentication authentication) throws AuthenticationException { if (authentication == null) { throw new InvalidTokenException("Invalid token (token not found)"); } //N 提取 access_token String token = (String) authentication.getPrincipal(); //N 验证 access_token 是否真的由授权服务器产生 OAuth2Authentication auth = tokenServices.loadAuthentication(token); if (auth == null) { throw new InvalidTokenException("Invalid token: " + token); } Collection resourceIds = auth.getOAuth2Request().getResourceIds(); //N 校验该令牌是否拥有访问资源服务器的权限 if (resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) { throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + resourceId + ")"); } //N 校验令牌和client 的scope是否相符 checkClientDetails(auth); if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) { OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails(); // Guard against a cached copy of the same details if (!details.equals(auth.getDetails())) { // Preserve the authentication details from the one loaded by token services details.setDecodedDetails(auth.getDetails()); } } auth.setDetails(authentication.getDetails()); auth.setAuthenticated(true); return auth; } //clientDetailsService 可能为null,后面我们单独说明。 private void checkClientDetails(OAuth2Authentication auth) { if (clientDetailsService != null) { //N 进入这里说明要么我们自己设置了clientDetailsService,要么资源服务与授权服务是同一个服务 ClientDetails client; try { client = clientDetailsService.loadClientByClientId(auth.getOAuth2Request().getClientId()); } catch (ClientRegistrationException e) { throw new OAuth2AccessDeniedException("Invalid token contains invalid client id"); } //数据库查询到的客户端配置的scope Set allowed = client.getScope(); //N 校验令牌的scope for (String scope : auth.getOAuth2Request().getScope()) { if (!allowed.contains(scope)) { throw new OAuth2AccessDeniedException( "Invalid token contains disallowed scope (" + scope + ") for this client"); } } } } }
接下来我们继续探索 tokenServices.loadAuthentication(token);方法都做了什么。5 ResourceServerTokenServices
ResourceServerTokenServices 的默认实现是 DefaultTokenServices, 这段代码可以在ResourceServerSecurityConfigurer 中看到,当然,如果我们配置了 resourceTokenServices,在 if 中则会直接返回配置的实现。public final class ResourceServerSecurityConfigurer extends SecurityConfigurerAdapter { ...... private ResourceServerTokenServices tokenServices(HttpSecurity http) { if (resourceTokenServices != null) { return resourceTokenServices; } DefaultTokenServices tokenServices = new DefaultTokenServices(); tokenServices.setTokenStore(tokenStore()); tokenServices.setSupportRefreshToken(true); tokenServices.setClientDetailsService(clientDetails()); this.resourceTokenServices = tokenServices; return tokenServices; } private ClientDetailsService clientDetails() { //获取授权服务设置的ClientDetailsService return getBuilder().getSharedObject(ClientDetailsService.class); } ...... }
Security 为我们提供了两个 ResourceServerTokenServices 子类。
5.1 DefaultTokenServices
通过 tokenStore 可以推测出,这个必须要和授权服务器能够访问相同的存储才行。(JwtTokenStore比较特殊,它并没有存储token,因为验证jwt只需要对称密钥或者公钥就可以,感兴趣的小伙伴可以看 JwtTokenStore 的源码)public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException, InvalidTokenException { //N 从存储中读取access_token,这里 JwtTokenStore 实现的比较特殊,是返回接收到的jwt //N 显然这要求授权服务器和资源服务器能否访问相同的存储,例如DB 或者redis OAuth2AccessToken accessToken = this.tokenStore.readAccessToken(accessTokenValue); if (accessToken == null) { throw new InvalidTokenException("Invalid access token: " + accessTokenValue); } else if (accessToken.isExpired()) { //令牌过期处理 this.tokenStore.removeAccessToken(accessToken); throw new InvalidTokenException("Access token expired: " + accessTokenValue); } else { //N 验证token ,如果是 JwtTokenStore ,验证jwt签名 OAuth2Authentication result = this.tokenStore.readAuthentication(accessToken); if (result == null) { throw new InvalidTokenException("Invalid access token: " + accessTokenValue); } else { //这里如果我们自己没有配置clientDetailsService,默认共享授权服务的,可能是null if (this.clientDetailsService != null) { String clientId = result.getOAuth2Request().getClientId(); try { this.clientDetailsService.loadClientByClientId(clientId); } catch (ClientRegistrationException var6) { throw new InvalidTokenException("Client not valid: " + clientId, var6); } } return result; } } }
这里说明一下 clientDetailsService,上面我们提到 OAuth2AuthenticationManager 和 DefaultTokenServices 中的this.clientDetailsService 都可能是 null 的问题。这是因为他们的取值都是 ResourceServerSecurityConfigurer 中的如下方法,getSharedObject获取的是授权服务配置的 ClientDetailsService。当授权服务与资源服务不是同一个服务的时候,getSharedObject 就会取不到值。 private ClientDetailsService clientDetails() { //获取授权服务设置的ClientDetailsService return getBuilder().getSharedObject(ClientDetailsService.class); }
在上面分析令牌生成时,授权服务只是提到了 AuthorizationServerEndpointsConfigurer 的配置,这里我们补充一下另外两个配置。@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Resource private DataSource dataSource; /** * 对auth2 提供的接口做访问规则配置和添加自定义过滤器 */ @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security //允许表单认证,对于接口/oauth/token,如果开启此配置,并且url中有client_id和client_secret会触发ClientCredentialsTokenEndpointFilter用于校验客户端是否有权限 .allowFormAuthenticationForClients() //设置接口/oauth/check_token 访问权限,默认denyAll(),资源服务器可以调用这个验证token,如果是jwt,资源服务器也可以自己通过密钥验证 .checkTokenAccess("isAuthenticated()") //提供jwt的公钥接口 /oauth/token_key .tokenKeyAccess("permitAll()") //N 添加自定义的过滤器 //通过AbstractAuthenticationProcessingFilter断点的additionalFilter可以看到该过滤器再链中的位置 //.addTokenEndpointAuthenticationFilter(new MyFilterFive()) //.passwordEncoder() ; } /** * 接入端管理配置,对应的是 oauth_client_details 表,实体是 BaseClientDetails */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { /*clients .inMemory() //client_1是客户端授权方式 .withClient("client_1")//接入端id .secret(new BCryptPasswordEncoder().encode("123456"))//接入端密钥 //.resourceIds(DEMO_RESOURCE_ID)//资源id .authorizedGrantTypes("client_credentials", "refresh_token")//授权方式 .scopes("select")//访问域 .authorities("client");//权限*/ clients.jdbc(dataSource); //clients.withClientDetails(new JdbcClientDetailsService(dataSource)); } /** * SpringSecurity-OAuth2 默认提供以下端口 * /oauth/authorize:授权端口 * /oauth/token:令牌端口 * /oauth/confirm_access:用户确认授权提交端口 * /oauth/error:授权服务错误信息端口 * /oauth/check_token:用于资源服务器访问的令牌解析端口 * /oauth/token_key:提供公有秘钥端口,如果使用的是 JWT 令牌的话 * pathMapping 可以映射成其他地址 */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .pathMapping("/oauth/token","/cloneli/token") .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)//允许的请求方式 //tokenStore默认内存存储,重启服务token就会失效 .tokenStore(new InMemoryTokenStore()) .reuseRefreshTokens(true) //.tokenEnhancer() .accessTokenConverter(jwtAccessTokenConverter()) //用于配置密码式的授权方式,如果不设置,密码模式请求token是,token为null,TokenEndpoint会提示不支持password授权模式,这里配置就是parent AuthenticationManager //.authenticationManager(authenticationManager()) /*.tokenGranter(new TokenGranter() { @Override public OAuth2AccessToken grant(String s, TokenRequest tokenRequest) { return null; } })*/ ; } }
在上面AuthorizationServerSecurityConfigurer 的配置中,可以看到 我们设置了 "/oauth/check_token" 接口.checkTokenAccess("isAuthenticated()"),即需要授权才能访问该接口。
而权限的配置就在下面 AuthorizationServerSecurityConfiguration 的方法中,同时有包括setSharedObject 的ClientDetailsService,因此授权服务和资源服务不是同一个服务,也就没有@EnableAuthorizationServer注解,就不会执行setSharedObject 的操作,这就是为什么getSharedObject(ClientDetailsService.class)可能是null的原因。@Configuration @Order(0) //ClientDetailsServiceConfiguration 提供clientDetailsService的bean @Import({ ClientDetailsServiceConfiguration.class, AuthorizationServerEndpointsConfiguration.class }) public class AuthorizationServerSecurityConfiguration extends WebSecurityConfigurerAdapter { ...... @Autowired private ClientDetailsService clientDetailsService; ...... @Override protected void configure(HttpSecurity http) throws Exception { AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer(); FrameworkEndpointHandlerMapping handlerMapping = endpoints.oauth2EndpointHandlerMapping(); http.setSharedObject(FrameworkEndpointHandlerMapping.class, handlerMapping); configure(configurer); http.apply(configurer); String tokenEndpointPath = handlerMapping.getServletPath("/oauth/token"); String tokenKeyPath = handlerMapping.getServletPath("/oauth/token_key"); String checkTokenPath = handlerMapping.getServletPath("/oauth/check_token"); if (!endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) { UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class); endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService); } // @formatter:off http .authorizeRequests() .antMatchers(tokenEndpointPath).fullyAuthenticated() //设置接口的访问权限 .antMatchers(tokenKeyPath).access(configurer.getTokenKeyAccess()) .antMatchers(checkTokenPath).access(configurer.getCheckTokenAccess()) .and() .requestMatchers() .antMatchers(tokenEndpointPath, tokenKeyPath, checkTokenPath) .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER); // @formatter:on //setSharedObject clientDetailsService http.setSharedObject(ClientDetailsService.class, clientDetailsService); } ...... } //没有@EnableAuthorizationServer注解,就不会执行AuthorizationServerSecurityConfiguration的方法 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({AuthorizationServerEndpointsConfiguration.class, AuthorizationServerSecurityConfiguration.class}) public @interface EnableAuthorizationServer { }5.2 RemoteTokenServices
见名知意,这个需要调用其他服务验证 access_token,显然能提供这个服务的应该就是授权服务了。授权服务提供了 CheckTokenEndpoint 用于验证 access_token 的接口如下,而其底层实现也是 resourceServerTokenServices 。/oauth/check_token
调用授权服务器的 "/oauth/check_token" 接口,我们上面提到需要授权服务器设置访问权限,这里用的是客户端模式。@Override public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException { MultiValueMap formData = new LinkedMultiValueMap(); formData.add(tokenName, accessToken); HttpHeaders headers = new HttpHeaders(); //N 设置请求头,客户端凭证,访问授权服务器需要认证 headers.set("Authorization", getAuthorizationHeader(clientId, clientSecret)); //N 设置请求地址,并发送请求获取验证结果 Map map = postForMap(checkTokenEndpointUrl, formData, headers); if (map.containsKey("error")) { logger.debug("check_token returned error: " + map.get("error")); throw new InvalidTokenException(accessToken); } Assert.state(map.containsKey("client_id"), "Client id must be present in response from auth server"); //N 构造 OAuth2Authentication 对象 return tokenConverter.extractAuthentication(map); }5.3 LocalTokenServices (自定义)
当然如果上面两种校验 access_token 的实现不能满足项目需求,也可以自定义自己的ResourceServerTokenServices 实现类,重写验证access_token的逻辑。
综上,整个令牌验证的流程就是 ResourceServerSecurityConfigurer指定了OAuth2AuthenticationProcessingFilter过滤器,过滤器调用 AuthenticationManager, AuthenticationManager 又调用 ResourceServerTokenServices 实现令牌验证,其中具体实现我们可以通过资源服务配置进行修改。
到这里,令牌验证通过,Authentication的信息就会放到线程变量SecurityContext中,然后过滤器就会放行请求。
不过验证令牌后得到的权限信息还没有用到。public interface Authentication extends Principal, Serializable { Collection<? extends GrantedAuthority> getAuthorities(); ...... }
这就要提到@EnableGlobalMethodSecurity系列的注解了,我们以后再说。
华为有什么黑科技?感谢您的阅读。华为有什么黑科技,先要说说黑科技是什么意思,黑科技泛指让人耳目一新的新技术新硬件新工艺新材料等,也可以在一个东西原有的基础上进行改造创新,使得它会变得更好。当然每个人
你对当前世界局势有什么看法?可以说当前世界是人类发展遇到前听未有的瓶颈和挑战。1人类自顾发展过度开采,造成资源严重匮乏,使得世界各国经济低迷不前,失业率大幅上升物价狂涨,从而倒至世界局势动荡和不稳。2环境污染
第4章流程控制分支结构引言1流程控制的作用流程控制语句是用来控制程序中各语句执行顺序的语句,可以把语句组合成能完成一定功能的小逻辑模块。2控制语句的分类控制语句分为三类顺序选择和循环。顺序结构代表先执行
ArrayList底层原理和重点代码分析ArrayList相关类图ArrayList是Java中常用的一种数据结构,底层使用数组实现。它的特点是支持动态扩容和随机访问,但是对于往中间插入或者删除,效率相对较低。下面我们来
以太坊创始人如何用区块链技术重塑金融世界头条创作挑战赛VitalikButerin是一位年轻而有远见的创业家,他的创新思想和领导能力使他成为了以太坊的创始人。他的创业故事充满了挑战和奋斗,但最终他的愿景和努力将以太坊推向
学习回顾ISIS路由渗透在ISIS协议中规定,L1区域必须且只能与L2区域相连,不同的L1区域之间不直接相连。而且,缺省情况下,L1区域内的路由信息可通过L12路由器发布到L2区域,(即L2路由器知道整个
中远海运为纸浆客户提供一致性区块链电子提单服务近日,中远海运智慧轮装载着客户LDC的溶解浆从巴西波图赛尔港起航,并通过IQAXeBL区块链电子提单系统为客户签发了非集装箱区块链电子提单。自此,中远海运特运为客户LDC签发区块链
女人冬天穿短裙打底裤,是什么心理?这是一种爱美的心理。爱美之心人皆有之,尤其是女人爱美好像是他们的专利,在这个世界上没有哪一个女人不爱美的,不爱美的女人,她自己可能本身就不会收拾的很美,丝毫引不起人们的关注,只有爱
这么多年,有没有一首歌直到现在依然让你记忆有新?谢邀!我是词曲作者洪华(网名洪福齐华),抽空来回答这么多年,有没有一首歌直到现在依然让你记忆犹新?大家应该知道,有些爱情歌曲,不仅仅是狭义上的爱情歌曲,还是广义上的爱情歌曲呢!比如
琅琊榜好看还是陈情令好看?都好看。琅琊榜是政治权谋戏,剧情紧凑,演员基本都是老戏骨,演技自然了得。陈情令是武侠剧,讲求画面美,演员基本都是新人,虽然略显青涩,但角色契合度超标。两部戏,就像春天的玉兰和冬天的
你对哪个女演员百看不厌,觉得她越看越好看?我喜欢高圆圆,越看越耐看!我觉得中囯内地百看不厌,越看越好看的演员非范冰冰莫属!她是娱乐圈公认的美女!她有一双又大又亮清澈透明的眼睛,有一张极其端正的瓜子脸,她肤如雪脂,青丝如云她
郑爽持股公司经营异常天眼查App显示,近日,上海鲸乖乖人工智能科技有限公司因未按规定期限公示年报,被上海市闵行区市场监督管理局列入经营异常名录。该公司成立于2019年1月,法定代表人为张恒,注册资本1
新能源汽车为什么独爱空气悬挂?是确有所需还是空有其表?有一说一,坦白一点说,新能源汽车真正需要的不是空气悬挂的性能,而是空气悬挂的名气,为什么这么说,因为S级用了,就是这么简单。空气悬挂其实是一个很粗糙的俗称,悬挂组件其实分成连杆,弹
甄嬛传皇上怀疑甄嬛和果郡王时,苏培盛为什么没给甄嬛报信?必追古装偶像剧自从锦溪跟了苏培盛,苏培盛就是甄嬛这边的人了,那皇上怀疑甄嬛和果郡王时,为什么苏培盛不去给甄嬛报信呢?如果他告诉了甄嬛皇上的计划,果郡王也许不会一再中计,就不会死了。
揭秘古代皇帝采选美女不为人知的四大途径后宫粉黛如云,为皇帝一人所有,无论大老婆小老婆,还是没有老婆名分却随时可以被占有玩弄和抛弃的无数后宫美女,这么多的后宫女子从何而来?从帝王选取后妃宫人的途径上看,有正式的规范化的礼
清末老照片讨饭的乞丐,监狱里的犯人,相貌清秀的庆亲王的长女讨饭的乞丐,不知从哪里讨来的毯子,裹在身上。手里拿着讨饭的家什,拄着打狗棒。在镜头面前,两眼无神,行将就木。为了生计,背井离乡四处乞讨的乞丐随处可见。监狱里的犯人,个个都戴着脚枷,
高高在上只有一种结局,李宁就是典型案例当一个品牌被推向大众舆论的高潮时,品牌应当如何回应?在当今时代,这个问题始终是大部分企业和品牌都在思索的问题。快节奏的网络化时代,任何的信息都会以极快的速度传播蔓延,当自身成为舆论
本周下跌原因找到了本周A股的下跌几乎超出所有网络上财经大V的意料之外,大部分的人都认为上周的2944和再上上周的2934是双底,并连篇累牍分析一通,结果本周五的一根大阴线狠狠地扇了一巴掌,不知道被扇
2022年世界互联网大会乌镇峰会将于11月9日至11日举办中国青年网北京10月31日电(记者刘尚君)记者10月31日从2022年世界互联网大会乌镇峰会新闻发布会上获悉,2022年世界互联网大会乌镇峰会将于11月9日至11日举行。本次大会主
期货市场能被准确预测吗我是交易员K,点击关注,每天分享期货交易的干货,陪你洞见交易真相,品味期货人生我最开始做交易的时候,身边也有不少的大神,他们在某一段时间总是表现的洋洋得意,交流时会提及自己的预测有
魅族自动泊车专利公布无需车位标线即可精确泊车天眼查App显示,近日,珠海市魅族科技有限公司自动泊车方法装置设备及存储介质专利公布。摘要显示,该实施例通过获取车辆预设范围的点云数据,基于点云数据进行车位识别。在得到至少一个尺寸
浅谈造车新势力之小鹏汽车是否值得买最近看了个新闻,大致意思是讲述了小鹏汽车的创始人何总的创业史,这里简单和大家分享下,何总最早于2004年创立uc浏览器,通过10年的艰苦奋斗,将自己一手创办的企业卖身于阿里集团,此