应用办公生活信息教育商业
投稿投诉
商业财经
汽车智能
教育国际
房产环球
信息数码
热点科技
生活手机
晨报新闻
办公软件
科学动态
应用生物
体育时事

SpringSecurity与RBAC用户模型

  一、介绍Security
  官方原话SpringSecurityisaframeworkthatprovidesauthentication,authorization,andprotectionagainstcommonattacks即SpringSecurity是一个提供身份验证、授权和防止常见攻击的框架。它是Spring提供的一个安全框架,可以根据使用者需要定制相关验证授权操作,配合SpringBoot可以快速开发一套完善的权限系统。二、快速上手创建一个SpringBoot项目并导入如下依赖或点击下载示例代码dependencygroupIdorg。springframework。bootgroupIdspringbootstartersecurityartifactIddependencydependencygroupIdorg。springframework。bootgroupIdspringbootstarterwebartifactIddependency复制代码运行SpringBoot应用程序
  若是正确启动了,可以看到SpringSecurity生成了一段默认密码。。。。2022091323:56:07。841WARN19924〔main〕。s。s。UserDetailsServiceAutoConfiguration:Usinggeneratedsecuritypassword:70a36ac670c14f72822c71165988c56e。。。复制代码访问http:localhost:8080会跳转到login登录页面,输入账号(user)密码(控制台自动生成的密码)以继续访问。
  SrpingSecurity主要解决的问题是安全访问控制,其实现原理是通过Filter对进入系统的请求进行拦截。当初始化SpringSecurity时,它创建了一个名为springSecurityFilterChain的Servlet过滤器,负责程序中的所以安全控制。三、基本原理DelegatingFilterProxy
  从必要知识里我们知道了Filter的工作原理,在Spring中使用自定义的Filter有个问题那就是Filter必须在Servlet容器启动前就注册好,但是Spring使用ContextLoaderListener来加载SpringBean,于是设计了DelegatingFilterProxy。本质上来说DelegatingFilterProxy就是一个Filter,其间接实现了Filter接口,它嵌入在ServletFilterChain中,但是在doFilter中其实调用的从Spring容器中获取到的代理Filter的实现类delegate。
  FilterChainProxy和SecurityFilterChain
  FilterChainProxy是SpringSecurity提供的一个特殊Filter,DelegatingFilterProxy并不是直接实例化和调用SpringSecurityFilter,而是构建了一个FilterChainProxy,当有请求进来就会去执行doFilter方法调用SecurityFilterChain所包含的各个Filter,同时这些Filter作为Bean被Spring管理,它是SpringSecurity使用的核心。
  此外,SecurityFilterChain提供了更大的灵活性,Servlet容器中,仅根据URL调用过滤器。但是,FilterChainProxy可以利用RequestMatcher接口,根据HttpServletRequest中的任何内容确定调用,比原生的Servlet更灵活,此外,FilterChainProxy可以构建多条SecurityFilterChain,你的应用程序可以为不同的情况提供完全独立的配置,如下图所示。
  过滤器链中主要的几个过滤器及其作用SecurityContextPersistenceFilter:这个Filter是整个拦截过程的入口,会在请求开始时从配置好的SecurityContextRepository中获取SecurityContext,然后把它设置给SecurityContextHolder。在请求完成后将SecurityContextHolder持有的SecurityContext再保存到配置好的SecurityContextRepository,同时清除securityContextHolder所持有的SecurityContext。UsernamePasswordAuthenticationFilter:用于处理来自表单提交的认证。该表单必须提供对应的用户名和密码,其内部还有登录成功或失败后进行处理的AuthenticationSuccessHandler和AuthenticationFailureHandler,这些都可以根据需求做相关改变;。LogoutFilter:用来处理实现用户登出和清除认证信息工作,登出成功后执行LogoutSuccessHandler,这里可以自定义实现一些功能。FilterSecurityInterceptor:是用于保护web资源的,使用AccessDecisionManager对当前用户进行授权访问ExceptionTranslationFilter:能够捕获来自FilterChain所有的异常,并进行处理。但是它只会处理两类异常:AuthenticationException和AccessDeniedException,其它的异常它会继续抛出。异常处理
  首先,ExceptionTranslationFilter调用FilterChain。doFilter(request,response)来调用应用程序的其余部分。如果用户未通过身份验证或者是AuthenticationException,则启动身份验证。SecurityContextHolder被清除HttpServletRequest保存在RequestCache中。当用户成功认证后,RequestCache用于重放原始请求。AuthenticationEntryPoint用于启动身份验证。例如,它可能重定向到登录页面或BASIC认证等。否则,如果是AccessDeniedException,则拒绝访问。调用AccessDeniedHandler来处理拒绝访问。表单登录
  以上示例在未授权的情况下访问会经过以下安全过滤器:Securityfilterchain:〔DisableEncodeUrlFilterWebAsyncManagerIntegrationFilterSecurityContextPersistenceFilterHeaderWriterFilterCsrfFilterLogoutFilterUsernamePasswordAuthenticationFilterDefaultLoginPageGeneratingFilterDefaultLogoutPageGeneratingFilterBasicAuthenticationFilterRequestCacheAwareFilterSecurityContextHolderAwareRequestFilterAnonymousAuthenticationFilterSessionManagementFilterExceptionTranslationFilterFilterSecurityInterceptor〕复制代码
  当没有登录的时候默认是anonymousUser匿名用户,经过一些列过滤器处理后,最后由FilterSecurityInterceptor进行权限校验授权,AccessDecisionManager进行授权投票,匿名用户不允许访问该接口,请求被拒绝重定向到登录页面,接着由DefaultLoginPageGeneratingFilter(自定义表单则不会初始化这个Filter)生成默认登录界面输出到浏览器。登录时经过UsernamePasswordAuthenticationFilter,只要用户请求满足该过滤器要求,则认证成功,接着是授权成功访问通过。
  每个过滤器都有不同的功能,组织在一起形成了强大的安全体系,你可以在过滤链中自定义过滤器,里面的逻辑我就不一一细说了没啥好讲的,官方文档中都有介绍。下面讲讲我自己的一些实现吧。四、我实现思路是什么,我是怎么实现的
  背景:拓展SpringSecurity实现基于Token的API认证授权基础程序
  采用的广为熟知的RBAC模型,基于角色的访问控制(RoleBasedAccessControl)
  拓展点:禁用CSRF(有个过滤器校验会报错)、会话管理设置为无状态STATELESS(因为我们要自定义处理登录注销逻辑)自定义UserDetailsService重写loadUserByUsername方法,从数据库中读取账号信息添加自定义Token认证过滤器自定义登录成功和失败处理器successHandler与failureHandler自定义注销处理器LogoutSuccessHandler自定义异常处理器AuthenticationEntryPoint与AccessDeniedHandler自定义AuthorizationManager
  开发调试可以设置一下日志输出级别,这样能助于我们更快地分析和排查问题:logging:level:org。springframework。web:traceorg。springframework。security:trace复制代码
  另外EnableWebSecurity这个注解debug属性设置为true也能看到更多的日志信息,这对我们很有帮助。SecurityConfiguration核心配置类EnableWebSecurity(debugfalse)publicclassSecurityConfiguration{privatefinalAppUserDetailsServiceuserDetailsService;privatefinalAbstractStringCacheStorecacheStore;privatefinalAuthenticationTokenFilterauthenticationTokenFilter;privatefinalPermissionAuthorizationManagerRequestAuthorizationContextpermissionAuthorizationManager;publicSecurityConfiguration(AppUserDetailsServiceuserDetailsService,AbstractStringCacheStorecacheStore,PermissionAuthorizationManagerRequestAuthorizationContextpermissionAuthorizationManager){this。userDetailsServiceuserDetailsService;this。cacheStorecacheStore;this。permissionAuthorizationManagerpermissionAuthorizationManager;this。authenticationTokenFilternewAuthenticationTokenFilter(cacheStore,userDetailsService);}BeanpublicWebSecurityCustomizerwebSecurityCustomizer(){return(web)web。ignoring()SpringSecurityshouldcompletelyignoreURLsstartingwithresources。antMatchers(resources);}BeanpublicSecurityFilterChainsecurityFilterChain(HttpSecurityhttp)throwsException{http。authorizeHttpRequests()。antMatchers(HttpMethod。OPTIONS,)。permitAll()。antMatchers()。permitAll()。antMatchers(userinfo)。authenticated()需要认证。anyRequest()。access(permissionAuthorizationManager)动态权限认证。and()。userDetailsService(userDetailsService)。formLogin()。permitAll()。successHandler(newCustomizeAuthenticationSuccessHandler(cacheStore))。failureHandler(newAuthenticationEntryPointFailureHandler(newCustomizeAuthenticationEntryPoint()))。and()。logout()。logoutSuccessHandler(newCustomizeLogoutSuccessHandler(cacheStore))。and()。exceptionHandling()。authenticationEntryPoint(newCustomizeAuthenticationEntryPoint())。accessDeniedHandler(newCustomizeAccessDeniedHandler())。and()。csrf()。disable()。sessionManagement()。sessionCreationPolicy(SessionCreationPolicy。STATELESS)。and()。addFilterBefore(authenticationTokenFilter,UsernamePasswordAuthenticationFilter。class)。addFilterBefore(authenticationTokenFilter,LogoutFilter。class);returnhttp。build();}复制代码
  释义:AuthenticationTokenFilterToken认证过滤器(除了自定义开放的接口外都会被调用)PermissionAuthorizationManager动态权限授权管理器(基于角色与资源权限表)CustomizeAuthenticationSuccessHandler登录处理器(登录成功后被调用用于生成Token)
  CustomizeLogoutSuccessHandler注销处理器(注销成功后被调用用于清除Toekn)CustomizeAuthenticationEntryPoint认证失败处理器(认证出现异常被调用)CustomizeAccessDeniedHandler授权失败处理器(授权出现异常被调用,如权限不足以访问某接口)AbstractStringCacheStore缓存类(用于缓存Token)CustomizeAuthenticationSuccessHandler登录处理器Slf4jpublicclassCustomizeAuthenticationSuccessHandlerimplementsAuthenticationSuccessHandler{privatefinalAbstractStringCacheStorecacheStore;Expiredseconds。privatestaticfinalintACCESSTOKENEXPIREDSECONDS243600;privatestaticfinalintREFRESHTOKENEXPIREDDAYS30;publicCustomizeAuthenticationSuccessHandler(AbstractStringCacheStorecacheStore){this。cacheStorecacheStore;}OverridepublicvoidonAuthenticationSuccess(HttpServletRequestrequest,HttpServletResponseresponse,Authenticationauthentication)throwsIOException{AppUserDetailsuserDetails(AppUserDetails)SecurityContextHolder。getContext()。getAuthentication()。getPrincipal();GeneratenewtokenAuthTokentokennewAuthToken();token。setAccessToken(BottleUtils。randomUUIDWithoutDash());token。setExpiredIn(ACCESSTOKENEXPIREDSECONDS);token。setRefreshToken(BottleUtils。randomUUIDWithoutDash());Cachethosetokens,justforclearingcacheStore。putAny(SecurityUtils。buildAccessTokenKey(userDetails),token。getAccessToken(),ACCESSTOKENEXPIREDSECONDS,TimeUnit。SECONDS);cacheStore。putAny(SecurityUtils。buildRefreshTokenKey(userDetails),token。getRefreshToken(),REFRESHTOKENEXPIREDDAYS,TimeUnit。DAYS);CachethosetokenswithuseridcacheStore。putAny(SecurityUtils。buildTokenAccessKey(token。getAccessToken()),userDetails。getUserId(),ACCESSTOKENEXPIREDSECONDS,TimeUnit。SECONDS);cacheStore。putAny(SecurityUtils。buildTokenRefreshKey(token。getRefreshToken()),userDetails。getUserId(),REFRESHTOKENEXPIREDDAYS,TimeUnit。DAYS);response。setCharacterEncoding(utf8);response。setStatus(HttpServletResponse。SCOK);response。setContentType(MediaType。APPLICATIONJSONVALUE);response。getWriter()。write(JsonUtils。objectToJson(BaseResponse。ok(登录成功!,token)));}复制代码LogoutSuccessHandler注销处理器Slf4jpublicclassCustomizeLogoutSuccessHandlerimplementsLogoutSuccessHandler{privatefinalAbstractStringCacheStorecacheStore;publicCustomizeLogoutSuccessHandler(AbstractStringCacheStorecacheStore){this。cacheStorecacheStore;}OverridepublicvoidonLogoutSuccess(HttpServletRequestrequest,HttpServletResponseresponse,Authenticationauthentication)throwsIOException,ServletException{if(Objects。isNull(authentication)){return;}AppUserDetailsuserDetails(AppUserDetails)authentication。getPrincipal();ClearaccesstokencacheStore。getAny(SecurityUtils。buildAccessTokenKey(userDetails),String。class)。ifPresent(accessToken{DeletetokencacheStore。delete(SecurityUtils。buildTokenAccessKey(accessToken));cacheStore。delete(SecurityUtils。buildAccessTokenKey(userDetails));});ClearrefreshtokencacheStore。getAny(SecurityUtils。buildRefreshTokenKey(userDetails),String。class)。ifPresent(refreshToken{cacheStore。delete(SecurityUtils。buildTokenRefreshKey(refreshToken));cacheStore。delete(SecurityUtils。buildRefreshTokenKey(userDetails));});response。setCharacterEncoding(utf8);response。setStatus(HttpServletResponse。SCOK);response。setContentType(MediaType。APPLICATIONJSONVALUE);response。getWriter()。write(JsonUtils。objectToJson(BaseResponse。ok(登出成功!,null)));log。info(Youhavebeenloggedout,lookingforwardtoyournextvisit!);}}复制代码AuthenticationTokenFilterToken认证过滤器Slf4jpublicclassAuthenticationTokenFilterextendsOncePerRequestFilter{privatestaticfinalStringAUTHENTICATIONSCHEMEBEARERBearer;privatefinalAbstractStringCacheStorecacheStore;privatefinalAppUserDetailsServiceappUserDetailsService;publicAuthenticationTokenFilter(AbstractStringCacheStorecacheStore,AppUserDetailsServiceappUserDetailsService){this。cacheStorecacheStore;this。appUserDetailsServiceappUserDetailsService;}OverrideprotectedvoiddoFilterInternal(HttpServletRequestrequest,HttpServletResponseresponse,FilterChainfilterChain)throwsServletException,IOException{GettokenfromrequestheaderStringaccessTokenrequest。getHeader(HttpHeaders。AUTHORIZATION);if(!StringUtils。hasText(accessToken)){DofilterfilterChain。doFilter(request,response);return;}if(!StringUtils。startsWithIgnoreCase(accessToken,AUTHENTICATIONSCHEMEBEARER)){thrownewBadCredentialsException(Token必须以bearer开头);}if(accessToken。equalsIgnoreCase(AUTHENTICATIONSCHEMEBEARER)){thrownewBadCredentialsException(Token不能为空);}GettokenbodyaccessTokenaccessToken。substring(AUTHENTICATIONSCHEMEBEARER。length()1);OptionalLongoptionalUserIdcacheStore。getAny(SecurityUtils。buildTokenAccessKey(accessToken),Long。class);if(!optionalUserId。isPresent()){log。debug(Token已过期或不存在〔{}〕,accessToken);filterChain。doFilter(request,response);return;}UserDetailsuserDetailsappUserDetailsService。loadUserById(optionalUserId。get());UsernamePasswordAuthenticationTokenauthenticationnewUsernamePasswordAuthenticationToken(userDetails,null,userDetails。getAuthorities());SecurityContextHolder。getContext()。setAuthentication(authentication);DofilterfilterChain。doFilter(request,response);}}复制代码CustomizeAuthenticationEntryPoint认证异常处理器Slf4jpublicclassCustomizeAuthenticationEntryPointimplementsAuthenticationEntryPoint{Overridepublicvoidcommence(HttpServletRequestrequest,HttpServletResponseresponse,AuthenticationExceptionauthException)throwsIOException,ServletException{BaseResponseObjecterrorDetailhandleBaseException(authException);errorDetail。setData(Collections。singletonMap(uri,request。getRequestURI()));response。setCharacterEncoding(utf8);response。setStatus(HttpStatus。UNAUTHORIZED。value());response。setContentType(MediaType。APPLICATIONJSONVALUE);response。getWriter()。write(JsonUtils。objectToJson(errorDetail));}privateBaseResponseObjecthandleBaseException(Throwablet){Assert。notNull(t,Throwablemustnotbenull);BaseResponseObjecterrorDetailnewBaseResponse();errorDetail。setStatus(HttpStatus。UNAUTHORIZED。value());if(log。isDebugEnabled()){errorDetail。setDevMessage(ExceptionUtils。getStackTrace(t));}if(tinstanceofAccountExpiredException){errorDetail。setMessage(账户过期);}elseif(tinstanceofDisabledException){errorDetail。setMessage(账号被禁用);}elseif(tinstanceofLockedException){errorDetail。setMessage(账户被锁定);}elseif(tinstanceofAuthenticationCredentialsNotFoundException){errorDetail。setMessage(用户身份凭证未找到);}elseif(tinstanceofAuthenticationServiceException){errorDetail。setMessage(用户身份认证服务异常);}elseif(tinstanceofBadCredentialsException){errorDetail。setMessage(t。getMessage());}else{errorDetail。setMessage(访问未授权);}returnerrorDetail;}}复制代码CustomizeAccessDeniedHandler授权异常publicclassCustomizeAccessDeniedHandlerimplementsAccessDeniedHandler{Overridepublicvoidhandle(HttpServletRequestrequest,HttpServletResponseresponse,AccessDeniedExceptionaccessDeniedException)throwsIOException,ServletException{BaseResponseObjecterrorDetailnewBaseResponse();errorDetail。setStatus(HttpStatus。FORBIDDEN。value());errorDetail。setMessage(禁止访问);response。setCharacterEncoding(utf8);response。setStatus(HttpServletResponse。SCFORBIDDEN);response。setContentType(MediaType。APPLICATIONJSONVALUE);response。getWriter()。write(JsonUtils。objectToJson(errorDetail));}}复制代码PermissionAuthorizationManager动态权限授权管理Slf4jComponentpublicclassPermissionAuthorizationManagerTimplementsAuthorizationManagerT{privatefinalAuthenticationTrustResolvertrustResolvernewAuthenticationTrustResolverImpl();privatefinalPermissionServicepermissionService;publicPermissionAuthorizationManager(PermissionServicepermissionService){this。permissionServicepermissionService;}OverridepublicAuthorizationDecisioncheck(Supplierauthentication,Tobject){DeterminesifthecurrentuserisauthorizedbyevaluatingifthebooleangrantedisGranted(authentication。get());if(!granted){returnnewAuthorizationDecision(false);}Collectionlt;?extendsGrantedAuthorityauthoritiesauthentication。get()。getAuthorities();SetStringauthorityauthorities。stream()。map(GrantedAuthority::getAuthority)。collect(Collectors。toSet());log。debug(username〔{}〕havroles:〔{}〕,authentication。get()。getName(),authority);RequestAuthorizationContextrequestAuthorizationContext(RequestAuthorizationContext)object;StringservletPathrequestAuthorizationContext。getRequest()。getRequestURI();log。debug(accessurl:{},servletPath);AppUserDetailsuserDetails(AppUserDetails)authentication。get()。getPrincipal();ListLongroleIdsuserDetails。getRoles()。stream()。map(Role::getId)。collect(Collectors。toList());ListPermissionpermissionspermissionService。listByRoleIds(roleIds);booleanagreeFlagpermissions。stream()。anyMatch(permissionisRouter(permission)permission。getUrl()。equals(servletPath));log。debug(checkresult:{},agreeFlag);returnnewAuthorizationDecision(agreeFlag);}privatebooleanisGranted(Authenticationauthentication){returnauthentication!nullisNotAnonymous(authentication)authentication。isAuthenticated();}privatebooleanisNotAnonymous(Authenticationauthentication){return!this。trustResolver。isAnonymous(authentication);}privatebooleanisRouter(Permissionpermission){return1。equals(permission。getType());}}复制代码五、示例
  登录成功POSTlogin?usernameuserpassword123456Host:localhost:8080response:{status:200,message:登录成功!,devMessage:null,data:{accesstoken:8430064e7d9b497c8b786a33b0524bc5,expiredin:86400,refreshtoken:8d2c6fb3489b47389a65cbf79f732f9a}}复制代码登录失败POSTlogin?usernameuserpassword123Host:localhost:8080response:{status:401,message:用户名或密码错误,devMessage:org。springframework。security。authentication。BadCredentialsException:用户名或密码错误。。。,data:{uri:login}}复制代码登录注销POSTlogoutHost:localhost:8080Authorization:Bearerb6422e3462224126a67f876b5f1b3a1eresponse:{status:200,message:登出成功!,devMessage:null,data:null}复制代码未登录或Token过期POSTlogoutHost:localhost:8080Authorization:Bearerb6422e3462224126a67f876b5f1b3a1eresponse:{status:401,message:访问未授权,devMessage:org。springframework。security。authentication。InsufficientAuthenticationException:Fullauthenticationisrequiredtoaccessthisresource。。。,data:{uri:userinfo}}复制代码权限不足GETadminHost:localhost:8080Authorization:Bearerf7a542c4899a4e6ea5039002a8f19110response:{status:403,message:禁止访问,devMessage:null,data:null}复制代码六、小结
  好了,就分享到这里了,希望对大家有所帮助,另外如有理解错误的地方请多多指教。SpringSecurity还有很多值得探索的功能,继续学习吧

南岳区小窗口彰显大情怀4月3日,南岳区政务中心个体工商户设立登记窗口迎来了一位特殊的客户,他在窗口徘徊许久,迟迟未选定自己需要咨询的窗口。南岳区市场监管局行政审批股工作人员及时察觉了异样,主动……聚焦315丨扫码价是售价的近10倍知名品牌展销店买不到经典款大众网海报新闻记者贺辉李义方张珈玮济南报道酒类商品尤其是白酒价格一直以来都存在同款产品不同渠道价格差别大、高端品牌加价提货、扫码价与实际价格差距过大等诸多问题。近日,大众……聚焦315外出旅游投资理财,这些法律提示请收好!为提升消费者法律意识,切实保障消费者合法权益,积极营造全民懂法、全民普法的良好氛围,315消费者权益日到来之际,通州法院民三庭法官梁睿诗前往北京市通州区玉桥南里南社区,以新类型……PlayStationPlus上的5款最佳独立游戏独立游戏允许玩家体验规模较小的游戏。通常,这些独立设计的标题的舒适性和复杂性,如果不是在某些方面可以与之竞争,也会超过AAA产品。无论是通过与生俱来的魅力还是对游戏设计更基本的……库里最后2分钟连得11分1封盖追进加时,加时独得9分勇士主场125116通过加时击败雄鹿,比赛第四节还剩2分05秒时,勇士还落后对手8分,不过之后斯蒂芬库里发挥出色,帮助球队将比赛追入加时,并在加时赛中率队取胜。第四节最后……你永远在我心里你永远在我心里写给相知相惜的异性知己作者:雅致丝语编辑:秋枫你,悄悄地走进我心里但我,从未说过爱你只怕,苍白了纯美的情谊你,轻轻地走进……摆脱束缚!美商海盗船推出新款HS55WIRELESS游戏耳机摆脱束缚、掌控你的节奏!美商海盗船推出新款HS55WIRELESS游戏耳机备受游戏玩家、内容创作者和PC装机者青睐的世界知名发烧组件及系统厂商美商海盗船(NASDAQ:C……王者荣耀技能和方向键需要互动的4位英雄,木兰光速沉默有技巧头条创作挑战赛文丨王者可儿原创在王者荣耀众多英雄中,很多英雄的技能都设计的比较有特色,比如百里守约、上官婉儿等等。还有一些英雄,他们在释放技能的时候还需要和移动键联……孕期能吃芒果吗,吃芒果会让宝宝变黄?孕期吃芒果的好处要知道芒果是我们常见的水果,很多小孩子和女性朋友都喜欢吃,因为它有着很香的气味,果肉也很软,制作成芒果奶昔、冰淇淋等更受欢迎。闺蜜小君从小就爱吃芒果,在她们老家还有各种吃芒果的……云南旅游昆明半日自由行散记(图)昆明作为云南省会城市,记者曾经多次光顾此地。特别是近日我的第八次云南行中的一次为期11天的滇东南之旅,更是首站就来到了这座令人感到十分温暖的春城。昆明机场(摄影:冯赣勇)……一图在手,社区美好生活尽在掌握!宝山区大场镇发布文旅地图4月8日是上海市民文化节城市美育日,在当天举行的行知读书会特别策划活动上,宝山区大场镇发布了行在大场、知其万象文旅地图。在地图前期策划过程中,大场镇成立了工作小组,对镇域……观点赛力斯许林新能源汽车生存艰难,有品牌卖一辆亏10万元文:懂车帝原创刘艺伟〔懂车帝原创行业〕3月31日4月2日,中国电动汽车百人会论坛(2023)在北京举行。在4月2日上午举行的以科技重塑新能源汽车用户体验为主题的高层……
茅台年底大放量,超10万瓶1499元飞天茅台开抢!春节送礼,名酒是首选,茅台、五粮液、洋河、泸州老窖。。。。。随便拎2瓶,长辈称心小辈有面。对于爱喝酱酒的人来说,飞天茅台是不二之选,但奈何价格甚高,平常在电商平台上抢购不……因使用GitHub,我们被取消了参赛资格整理郑丽媛出品CSDN(ID:CSDNnews)说到GitHub,相信每一个程序员都再熟悉不过。作为全球最大的开发者社区,如今GitHub拥有9400万开发者……众星时装周生图曝光杜鹃张嘉倪惊艳四座,刘雯丑蔡徐坤脸垮众星前往米兰参加时装周,各种国内外摄像师镜头聚焦,在无P无滤镜的高清生图里,真实颜值就很容易露馅。来看看谁扛住了?刘雯以模特身份出现在秀场,一向对时尚活动十拿九稳的大表姐……脸红心跳!夏花开分7。1,央视推荐,47岁言承旭再演偶像剧本月的荧屏毫无疑问是热闹的,足以称得上是暮春三月,万物生发的热闹。不仅剧集种类繁多,就连剧集质量平均都在水准之上。比如全网讨论度极高,几乎仙侠粉人人都在看的大IP《……东平县山东手造走进白佛山春会2月19日,正值东平县具有一千多年传统的白佛山春会进行中,山前的广场上游人如织,老少相携共踏春色。一个个摆满了精美艺术品的摊位前,众多群众正在聚精会神地欣赏着美丽的烙画作品、木……一声鸡鸣叫醒四国人一声鸡鸣叫醒四国人,一脚踏四国的县城,你知道在哪里吗?它被国家地理杂志称作中国最牛县城,它美女如云,却从不外嫁。它与阿富汗、塔吉克斯坦、巴基斯坦三个国家接壤,可以说是一声鸡鸣就……民间故事男子归家,遇黄鼠狼拦路讨封相助,黄鼠狼归家莫喝汤我是一名旅游爱好者,常年奔走于祖国的大江南北,参观过很多的名胜古迹,也游览过无数的名山胜水。尤其对祖国边疆地区的少数民族风情和民俗往事更加喜爱。由于常年的四处奔走,……今生有幸如果遇到了这辈子最爱的人,就带TA去看这六座日照金山众人皆赴海,唯我独向山。〔灵光一闪〕日照金山是一种关于大山的特别美丽的自然奇观,它只有在特定的时间特定的地点才能看到,一般发生在日出时分或者傍晚,我国境内日照金山奇观不胜……新疆吐鲁番杏花花开正当时游客共赴春天之约央视网消息:每年的3月中旬是新疆吐鲁番杏花花开时节,粉花、白花相伴相依,吸引了大量游客共赴一场杏花之约。画面中大片的杏花盛开在田间,粉白相间、千姿百态,行走在花丛间沐浴着……贵阳永乐乡万亩桃花竞相绽放3月18日,游客在永乐乡桃花林中跑步。瞿宏伦摄3月18日,游客在永乐乡桃花林中游览。瞿宏伦摄3月18日,一位俄罗斯籍媒体从业者在永乐乡桃花林中录制视频。瞿宏伦摄……4月6日精彩足球比赛伤停信息西超杯:巴萨VS皇马巴萨:8号罗德里(中场进攻核心,伤病)7号登贝莱(主力边锋,伤病)21号德容(中场出球核心,伤病)15号克里斯滕斯森(后防核心……电讯报芬兰企业家报价被曼联拒绝拉特克利夫已提交第二份报价直播吧3月24日讯《每日电讯报》今天撰文谈到了曼联收购的近况,其中曼联已经拒绝了芬兰企业家Zilliacus提出的报价,拉特克利夫已经提交了修改后的报价,阿勒萨尼团队则没有提交……
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网