商城认证服务

一、搭建认证服务环境

  结合我们前面介绍的商城的架构我们需要单独的搭建一个认证服务。

1.创建项目

  首先创建一个SpringBoot项目,然后添加对应的依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.12</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.msb.mall</groupId><artifactId>mall-auth-server</artifactId><version>0.0.1-SNAPSHOT</version><name>mall-auth_server</name><description>认证服务</description><properties><java.version>1.8</java.version><spring-cloud.version>2020.0.1</spring-cloud.version></properties><dependencies><!-- 公共依赖 --><dependency><groupId>com.msb.mall</groupId><artifactId>mall-commons</artifactId><version>0.0.1-SNAPSHOT</version><exclusions><exclusion><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

2.注册中心配置

  我们需要把认证服务注册到Nacos中,添加对应的依赖,然后完成对应的配置

# Nacos服务注册
spring:cloud:nacos:discovery:server-addr: 192.168.56.100:8848application:name: mall-auth_server# 统一的全局的--设置服务器响应给客户端的日期时间格式jackson:date-format: yyyy-MM-dd HH:mm:ssthymeleaf:cache: false # 关闭Thymeleaf的缓存
server:port: 30000

放开Nacos注册中心

然后启动测试

3.登录和注册页面

  然后我们整理登录和注册的相关资源,首先把登录和注册的模板文件拷贝进项目

然后把对应的静态文件拷贝到Nginx中。

然后我们需要在host文件中添加对应的配置

修改Nginx的反向代理的配置

然后重启Nginx的服务

然后修改网关服务

最后调整登录和注册页面的静态资源文件的路径.

注册服务的名称:msb-auth,启动对应的服务,测试

登录页面

注册页面

4.注册功能

4.1 手机验证码

  先处理验证码的页面,使其能够倒数操作

JS代码

$(function(){$("#sendCode").click(function(){if($(this).hasClass("d1")){// 说明正在倒计时}else{// 给指定的手机号发送验证码timeoutChangeStyle()}});})var num = 10function timeoutChangeStyle(){$("#sendCode").attr("class","d1")if(num == 0){// 说明1分钟到了,可以再次发送验证码了$("#sendCode").text("发送验证码")num= 10;$("#sendCode").attr("class","")}else{setTimeout('timeoutChangeStyle()',1000)$("#sendCode").text(num+"s后再次发送")}num --;}

4.2 短信验证接口

  通过阿里云的短信服务来实现。我们直接购买0元15次就可以了。https://www.aliyun.com/

进入到对应的管理控制台,查看对应的信息

通过短信供应商提供的相关的编程语言的开发模板开发即可

供应商提供了对应的HttpUtils工具类,我们需要下载保存到我们自己的项目中。

然后封装对应的发送验证码的接口

package com.msb.mall.third.utils;import lombok.Data;
import org.apache.http.HttpResponse;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;import java.util.HashMap;
import java.util.Map;/*** 短信组件*/
@ConfigurationProperties(prefix = "spring.cloud.alicloud.sms")
@Data
@Component
public class SmsComponent {private String host;private String path;private String method = "POST";private String appCode;/*** 发送短信验证码* @param phone 发送的手机号* @param code 发送的短信验证码*/public void sendSmsCode(String phone,String code){Map<String, String> headers = new HashMap<String, String>();//最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105headers.put("Authorization", "APPCODE " + appCode);//根据API的要求,定义相对应的Content-Typeheaders.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");Map<String, String> querys = new HashMap<String, String>();Map<String, String> bodys = new HashMap<String, String>();bodys.put("content", "code:"+code);bodys.put("phone_number", phone);bodys.put("template_id", "TPL_0000");try {HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);System.out.println(response.toString());//获取response的body//System.out.println(EntityUtils.toString(response.getEntity()));} catch (Exception e) {e.printStackTrace();}}
}

添加对应的属性信息

4.3 短信功能

  然后我们将短信功能串联起来

4.3.1 third服务

  我们需要在第三方服务中提供对外的接口服务

@RestController
public class SMSController {@Autowiredprivate SmsComponent smsComponent;/*** 调用短信服务商提供的短信API发送短信* @param phone* @param code* @return*/@GetMapping("/sms/sendcode")public R sendSmsCode(@RequestParam("phone") String phone,@RequestParam("code") String code){smsComponent.sendSmsCode(phone,code);return R.ok();}
}

4.3.2 认证服务

  我们需要在认证服务中通过feign来调用third中提供的短信服务,同时给客户端提供访问的接口

        <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>

放开注解

然后声明Feign的接口

定义对应的Controller

@Controller
public class LoginController {@Autowiredprivate ThirdPartFeginService thirdPartFeginService;@ResponseBody@GetMapping("/sms/sendCode")public R sendSmsCode(@RequestParam("phone") String phone){// 生成随机的验证码String code = UUID.randomUUID().toString().substring(0, 5);thirdPartFeginService.sendSmsCode(phone,code);return R.ok();}
}

4.3.3 客户端

  然后我们需要在页面中通过jQuery的异步提交来发送短信

4.3.4 验证码存储

  当我们把验证码通过短信的形式发送给你客户手机,然后我们需要把手机号和对应的验证码存储起来,后面可能会集群部署,这时我们把这个信息存在在Redis中。

添加配置

存储数据

搞定

4.3.5 60秒间隔

  针对验证码发送的间隔必须是60秒以上,这时我们可以在保存到Redis中的数据的值我们加上发送的时间来处理

模板页面也需要做出对应的处理

4.4 注册数据验证

  表单提交的注册数据我们通过JSR303来验证。

首先定义VO对象

package com.msb.mall.vo;import lombok.Data;
import org.hibernate.validator.constraints.Length;import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;/*** 注册用户的VO对象*/
@Data
public class UserRegisterVo {@NotEmpty(message = "账号不能为空")@Length(min = 3,max = 15,message = "账号必须是3~15位")private String userName; // 账号@NotEmpty(message = "密码不能为空")@Length(min = 3,max = 15,message = "密码必须是3~15位")private String password; // 密码@NotEmpty(message = "手机号不能为空")@Pattern(regexp = "^[1][3-9][0-9]{9}$",message = "手机号不合法")private String phone;  // 手机号@NotEmpty(message = "验证码不能为空")private String code;  // 验证码
}

然后就是控制器的逻辑代码

    @PostMapping("/sms/register")public String register(@Valid UserRegisterVo vo, BindingResult result, Model model){if(result.hasErrors()){// 表示提交的数据不合法List<FieldError> fieldErrors = result.getFieldErrors();Map<String,String> map = new HashMap<>();for (FieldError fieldError : fieldErrors) {String field = fieldError.getField();String defaultMessage = fieldError.getDefaultMessage();map.put(field,defaultMessage);}model.addAttribute("error",map);return "/reg";}// 表单提交的注册的数据是合法的return "redirect:/login.html";}

然后就是页面代码处理

验证码的校验

4.5 完整的注册功能

会员服务处理

控制器

   /*** 会员注册* @return*/@PostMapping("/register")public R register(@RequestBody MemberReigerVO vo){try {memberService.register(vo);}catch (UsernameExsitException exception){return R.error(BizCodeEnume.USERNAME_EXSIT_EXCEPTION.getCode(),BizCodeEnume.USERNAME_EXSIT_EXCEPTION.getMsg());}catch (PhoneExsitExecption exsitExecption) {return R.error(BizCodeEnume.PHONE_EXSIT_EXCEPTION.getCode(),BizCodeEnume.PHONE_EXSIT_EXCEPTION.getMsg());}catch (Exception e){return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg());}return R.ok();}

然后对应的Service

   /*** 完成会员的注册功能* @param vo*/@Overridepublic void register(MemberReigerVO vo) throws PhoneExsitExecption,UsernameExsitException{MemberEntity entity = new MemberEntity();// 设置会员等级 默认值MemberLevelEntity memberLevelEntity = memberLevelService.queryMemberLevelDefault();entity.setLevelId(memberLevelEntity.getId()); // 设置默认的会员等级// 添加对应的账号和手机号是不能重复的checkUsernameUnique(vo.getUserName());checkPhoneUnique(vo.getPhone());entity.setUsername(vo.getUserName());entity.setMobile(vo.getPhone());BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();String encode = encoder.encode(vo.getPassword());// 需要对密码做加密处理entity.setPassword(encode);// 设置其他的默认值this.save(entity);}

auth服务通过Fegin远程调用

远程调用

    @PostMapping("/sms/register")public String register(@Valid UserRegisterVo vo, BindingResult result, Model model){Map<String,String> map = new HashMap<>();if(result.hasErrors()){// 表示提交的数据不合法List<FieldError> fieldErrors = result.getFieldErrors();for (FieldError fieldError : fieldErrors) {String field = fieldError.getField();String defaultMessage = fieldError.getDefaultMessage();map.put(field,defaultMessage);}model.addAttribute("error",map);return "/reg";}else{// 验证码是否正确String code = (String)redisTemplate.opsForValue().get(SMSConstant.SMS_CODE_PERFIX + vo.getPhone());code = code.split("_")[0];if(!code.equals(vo.getCode())){// 说明验证码不正确map.put("code","验证码错误");model.addAttribute("error",map);return "/reg";}else{// 验证码正确  删除验证码redisTemplate.delete(SMSConstant.SMS_CODE_PERFIX + vo.getPhone());// 远程调用对应的服务 完成注册功能R r = memberFeginService.register(vo);if(r.getCode() == 0){// 注册成功return "redirect:http://msb.auth.com/login.html";}else{// 注册失败map.put("msg",r.getCode()+":"+r.get("msg"));model.addAttribute("error",map);return "/reg";}}}//return "redirect:/login.html";}

完成对应的服务注册

校验提示

5.登录功能

  通过最基础的登录操作来完成登录处理

登录页面处理

认证服务的处理

/*** 注册的方法* @return*/@PostMapping("/login")public String login(LoginVo loginVo , RedirectAttributes redirectAttributes){R r = memberFeginService.login(loginVo);if(r.getCode() == 0){// 表示登录成功return "redirect:http://msb.mall.com/home";}redirectAttributes.addAttribute("errors",r.get("msg"));// 表示登录失败,重新跳转到登录页面return "redirect:http://msb.auth.com/login.html";}

会员中心的认证逻辑

@RequestMapping("/login")public R login(@RequestBody MemberLoginVO vo){MemberEntity entity = memberService.login(vo);if(entity != null){return R.ok();}return R.error(BizCodeEnume.USERNAME_PHONE_VALID_EXCEPTION.getCode(),BizCodeEnume.USERNAME_PHONE_VALID_EXCEPTION.getMsg());}

service中的具体认证处理

    @Overridepublic MemberEntity login(MemberLoginVO vo) {// 1.根据账号或者手机号来查询会员信息MemberEntity entity = this.getOne(new QueryWrapper<MemberEntity>().eq("username", vo.getUserName()).or().eq("mobile", vo.getUserName()));if(entity != null){// 2.如果账号或者手机号存在 然后根据密码加密后的校验来判断是否登录成功BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();boolean matches = encoder.matches(vo.getPassword(), entity.getPassword());if(matches){// 表明登录成功return entity;}}return null;}

6.Auth2.0

  OAuth2.0是OAuth协议的延续版本,但不向前兼容OAuth 1.0(即完全废止了OAuth1.0)。 OAuth
2.0关注客户端开发者的简易性。要么通过组织在资源拥有者和HTTP服务商之间的被批准的交互动作代表用户,要么允许第三方应用代表用户获得访问的权限。

6.1 微博开放平台

地址:https://open.weibo.com/

创建应用

创建后的基本信息:

授权设置:

社交认证文档:

微博Web端授权的操作:

引导用户点击按钮跳转到对应的授权页面

点击授权按钮后查看回调接口的code信息

获取到了code信息:59d62e59e5ead5a4ea89c6f9cf212568

然后根据code信息我们可以去授权服务器获取对应的AccessToken。

https://api.weibo.com/oauth2/access_token?client_id=1093598037&client_secret=1085c8de04dee49e9bb110eaf2d3cf62&grant_type=authorization_code&redirect_uri=http://msb.auth.com/success.html&code=59d62e59e5ead5a4ea89c6f9cf212568

获取Token信息只支持POST方式提交

在PostMan中通过post方式提交成功获取到了对应的token信息

获取到了Token信息后我们就可以去资源服务器获取对象的信息

6.2 百度开放平台

地址:

Auth2.0操作:https://developer.baidu.com/wiki/index.php?title=docs/oauth

创建应用:http://developer.baidu.com/console#app/project

创建完成:

引导用户跳转到授权地址:

http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REGISTERED_REDIRECT_URI&scope=email&display=popup

地址修改为我们自己的:http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&client_id=MmvAkQM7HtrQnKDDhhmTSib5&redirect_uri=http://www.baidu.com&display=popup

获取到的Code信息

code:d789d0160b2fa99bb1f840002569526e

获取到对应的token信息

Token:121.6966ae0e0f3cd19fa36a375489342b08.YmfrSxYqsOt1eUoPzkC60yCsa7W09OmqTbPsuVL.zmdMFg

token访问地址:https://openapi.baidu.com/rest/2.0/passport/users/getLoggedInUser?access_token=121.6966ae0e0f3cd19fa36a375489342b08.YmfrSxYqsOt1eUoPzkC60yCsa7W09OmqTbPsuVL.zmdMFg

7.社交登录实现

7.1 code处理

  在后台服务中获取code并对应的获取Token信息

然后需要同步的调整引入的链接地址:

7.2 获取Token信息

  根据上一步获取的code信息,我们可以获取对应的Token信息

    @RequestMapping("/oauth/weibo/success")public String weiboOAuth(@RequestParam("code") String code) throws Exception {Map<String,String> body = new HashMap<>();body.put("client_id","1093598037");body.put("client_secret","1085c8de04dee49e9bb110eaf2d3cf62");body.put("grant_type","authorization_code");body.put("redirect_uri","http://msb.auth.com/oauth/weibo/success");body.put("code",code);// 根据Code获取对应的Token信息HttpResponse post = HttpUtils.doPost("https://api.weibo.com", "/oauth2/access_token", "post", new HashMap<>(), null, body);int statusCode = post.getStatusLine().getStatusCode();if(statusCode != 200){// 说明获取Token失败,就调回到登录页面return "redirect:http://msb.auth.com/login.html";}// 说明获取Token信息成功String json = EntityUtils.toString(post.getEntity());SocialUser socialUser = JSON.parseObject(json, SocialUser.class);// 注册成功就需要调整到商城的首页return "redirect:http://msb.mall.com/home.html";}

7.3 登录和注册

  表结构中新增对应的

然后在对应的实体对象中添加对应的属性

service中实现注册和登录的逻辑

    /*** 社交登录* @param vo* @return*/@Overridepublic MemberEntity login(SocialUser vo) {String uid = vo.getUid();// 如果该用户是第一次社交登录,那么需要注册// 如果不是第一次社交登录 那么就更新相关信息 登录功能MemberEntity memberEntity = this.getOne(new QueryWrapper<MemberEntity>().eq("social_uid", uid));if(memberEntity != null){// 说明当前用户已经注册过了 更新token和过期时间MemberEntity entity = new MemberEntity();entity.setId(memberEntity.getId());entity.setAccessToken(vo.getAccessToken());entity.setExpiresIn(vo.getExpiresIn());this.updateById(entity);// 在返回的登录用户信息中我们同步的也保存 token和过期时间memberEntity.setAccessToken(vo.getAccessToken());memberEntity.setExpiresIn(vo.getExpiresIn());return memberEntity;}// 表示用户是第一提交,那么我们就需要对应的来注册MemberEntity entity = new MemberEntity();entity.setAccessToken(vo.getAccessToken());entity.setExpiresIn(vo.getExpiresIn());entity.setSocialUid(vo.getUid());// 通过token调用微博开发的接口来获取用户的相关信息try {Map<String,String> querys = new HashMap<>();querys.put("access_token",vo.getAccessToken());querys.put("uid",vo.getUid());HttpResponse response = HttpUtils.doGet("https://api.weibo.com", "/2/users/show.json", "get", new HashMap<>(), querys);if(response.getStatusLine().getStatusCode() == 200){String json = EntityUtils.toString(response.getEntity());JSONObject jsonObject = JSON.parseObject(json);String nickName = jsonObject.getString("screen_name");String gender = jsonObject.getString("gender");entity.setNickname(nickName);entity.setGender("m".equals(gender)?1:0);}}catch (Exception e){}// 注册用户信息this.save(entity);return entity;}

7.4 登录的串联

在Auth服务中我们需要通过Feign来调用MemberService中的相关服务来完成最后的串联

    @RequestMapping("/oauth/weibo/success")public String weiboOAuth(@RequestParam("code") String code) throws Exception {Map<String,String> body = new HashMap<>();body.put("client_id","1093598037");body.put("client_secret","1085c8de04dee49e9bb110eaf2d3cf62");body.put("grant_type","authorization_code");body.put("redirect_uri","http://msb.auth.com/oauth/weibo/success");body.put("code",code);// 根据Code获取对应的Token信息HttpResponse post = HttpUtils.doPost("https://api.weibo.com", "/oauth2/access_token", "post", new HashMap<>(), null, body);int statusCode = post.getStatusLine().getStatusCode();if(statusCode != 200){// 说明获取Token失败,就调回到登录页面return "redirect:http://msb.auth.com/login.html";}// 说明获取Token信息成功String json = EntityUtils.toString(post.getEntity());SocialUser socialUser = JSON.parseObject(json, SocialUser.class);R r = memberFeginService.socialLogin(socialUser);if(r.getCode() != 0){// 登录错误return "redirect:http://msb.auth.com/login.html";}String entityJson = (String) r.get("entity");System.out.println("----------------->" + entityJson);// 注册成功就需要调整到商城的首页return "redirect:http://msb.mall.com/home";}

二、分布式session

1.session问题

2.SpringSession整合

  我们通过SpringSession来实现Session的共享,Session数据存储在Redis中

SpringSession的操作指南:

https://docs.spring.io/spring-session/docs/2.5.6/reference/html5/guides/boot-redis.html

导入相关的依赖

        <dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId></dependency>

设置对应的配置

最后我们需要添加对有的注解,放开操作

然后在Auth服务和商城首页都整合SpringSession后,我们再商城首页可以看到Session的数据,注意这儿是手动修改Cookie的域名

3.自定义Cookie

  通过自定义Cookie实现session域名的调整

@Configuration
public class MySessionConfig {/*** 自定义Cookie的配置* @return*/@Beanpublic CookieSerializer cookieSerializer(){DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();cookieSerializer.setDomainName("msb.com"); // 设置session对应的一级域名cookieSerializer.setCookieName("msbsession");return cookieSerializer;}/*** 对存储在Redis中的数据指定序列化的方式* @return*/@Beanpublic RedisSerializer<Object> redisSerializer(){return new GenericJackson2JsonRedisSerializer();}
}

4.单点登录案例演示

  xxl-sso案例代码地址:https://gitee.com/xuxueli0323/xxl-sso?_from=gitee_search

下载下来的代码解压缩后通过idea导入,然后修改server和simple中的属性文件,同时我们需要在host中设置对应的域名

127.0.0.1 ssoserver.com
127.0.0.1 msb1.com
127.0.0.1 msb2.com

在server的配置文件中,修改Redis的服务地址

然后在simple中修改server的地址和redis服务的地址

然后分别启动服务测试即可

【第十六篇】商城系统-认证系统构建相关推荐

  1. Python之路【第十六篇】:Django【基础篇】

    https://www.cnblogs.com/wupeiqi/articles/5237704.html Python之路[第十六篇]:Django[基础篇] Python的WEB框架有Django ...

  2. 秒杀多线程第十六篇 多线程十大经典案例之一 双线程读写队列数据

    本文配套程序下载地址为:http://download.csdn.net/detail/morewindows/5136035 转载请标明出处,原文地址:http://blog.csdn.net/mo ...

  3. CCIE-LAB-第十六篇-NAT+OSPF下发默认路由+校验配置(模块一结束篇章)

    CCIE-LAB-第十六篇-NAT+OSPF下发默认路由+校验配置(模块一结束篇章) 实际中,思科只会给你5个小时去做下面的全部配置 这个是CCIE-LAB的拓扑图 问题 翻译: 根据这些要求1为FA ...

  4. CCNA-第十六篇-综合实验

    CCNA-第十六篇-综合实验 环境以及拓扑图如下 TAG:个人说明,做到最后我才发现hostname打错了,IDC-1打成ISP-1了,不过也没关系,知道就行了,全部的IDC都打成ISP了 还有一个的 ...

  5. java监听数据库操作_第十六篇——JDBC操作数据库之监听器

    JavaWeb应用中,很多的地方都和session有关.因此session相关的事件监听器,在日常工作中非常有用. 有时候我们需要统计当前在线的人数和访问人数总数,此时就可以使用监听器技术来很简单的实 ...

  6. 生日祝福小程序_广告配音剪映零基础入门教程第二十六篇:如何给朋友制作生日祝福视频...

    经常听到小伙伴问到生日祝福视频怎么做,当然我想既然要为他人做生日祝福视频,那么这个人必定是自己身边比较重要的人,而生日又是每个人都是非常重要的,在这种充满意义的时刻,我们想给自己极其重要的人送上一份祝 ...

  7. 喜迎四十万访问量,自荐十六篇好博文

    喜迎四十万访问量,自荐十六篇好博文 作者:赖勇浩(http://blog.csdn.net/lanphaday) 2008年12月8日本博的访问量一举突破 40 万大关,猛回头,发现已经在 CSDN ...

  8. 第十六篇 Computed 计算属性的应用

    本篇内容主要讲计算属性的相关知识,如果从之前的有一直关注这一系列的文章内容的话,就会发现之前的一些案例可以用其他方法方式去做,比如 第八篇.第九篇 所提及到的模糊查询以及 第十四篇 实现购物车页面的一 ...

  9. 第十六讲:达索系统Delmia制造运营转型网络研讨会 | 达索系统百世慧

    DELMIA 由 3DEXPERIENCE 平台提供技术支持,可帮助各种行业的从业者和服务提供商将价值网络的虚拟世界与真实世界联系在一起,以开展协作.建模和优化,随后将成果付诸执行.卓越运营需要整个价 ...

最新文章

  1. CentOs下卸载程序
  2. 美多商城之支付(评价订单商品)
  3. redhat搭建NIS服务器
  4. 值对于int32太大或太小怎么解决_深孔加工10种常见问题及解决措施,条条都是宝贝!...
  5. Elasticsearch-kopf导览
  6. android横向排列 间隙,Android开发消除横向排列的多个Button之间的空隙
  7. hdfs文件如何导出到服务器,[Hadoop] 如何将 HDFS 文件导出到 Windows文件系统
  8. 买二手房子经济实惠。
  9. C#中的正则表达式引擎
  10. iOS中创建动态库及调用方法
  11. Lottie-iOS的应用及部分源码分析
  12. 雨棚板弹性法计算简图_旌阳移动推拉雨棚移动蓬定制
  13. LINUX SHELL使用变量控制循环
  14. 计算机基础知识上机题,计算机基础上机题库.doc
  15. 【转】通过《淘宝这十年》总结的脑图
  16. JAVA微信小程序医院预约挂号小程序系统毕业设计 开题报告
  17. 计算机显示器怎么调左右,电脑屏幕内容左右看不到边怎么调
  18. 微信开放平台开发第三方授权登陆:微信扫码登录
  19. 元引擎视频制作工具 一键生成原创视频软件
  20. 你值得拥有的一份面试题

热门文章

  1. GK888t打印机设置
  2. 基于数据驱动的交叉口精细化管理——以北京门头沟双峪路口为例
  3. 计算机网络安全-----身份认证
  4. iptables 窥探、扫盲
  5. echarts map 地图做出立体效果 | 简单的解
  6. 高职计算机考试试题与答案,2009高职高考计算机试题
  7. OpenGL教程翻译 第二十六课 法线纹理
  8. yolov5 教学入门使用
  9. 数据库进阶·如何针对所有用户数据中没有的数据去加入随机的数据-蜻蜓Q系统用户没有头像如何加入头像数据-优雅草科技kir
  10. SpringBoot+Vue实现前后端分离在线视频点播系统