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

dede如何生成网站源码镇江网站建设推广

dede如何生成网站源码,镇江网站建设推广,wordpress哪一年开始,做编程的 网站认证服务 1. 环境搭建 创建gulimall-auth-server模块,导依赖,引入login.html和reg.html,并把静态资源放到nginx的static目录下 2. 注册功能 (1) 验证码倒计时 //点击发送验证码按钮触发下面函数 $("#sendCode").click(function () {//如果有disabled,说明最近…

认证服务

1. 环境搭建

创建gulimall-auth-server模块,导依赖,引入login.htmlreg.html,并把静态资源放到nginx的static目录下

2. 注册功能

(1) 验证码倒计时

在这里插入图片描述

//点击发送验证码按钮触发下面函数
$("#sendCode").click(function () {//如果有disabled,说明最近已经点过,则什么都不做if($(this).hasClass("disabled")){}else {//调用函数使得当前的文本进行倒计时功能timeOutChangeStyle();//发送验证码var phone=$("#phoneNum").val();$.get("/sms/sendCode?phone="+phone,function (data){if (data.code!=0){alert(data.msg);}})}})let time = 60;function timeOutChangeStyle() {//开启倒计时后设置标志属性disable,使得该按钮不能再次被点击$("#sendCode").attr("class", "disabled");//当时间为0时,说明倒计时完成,则重置if(time==0){$("#sendCode").text("点击发送验证码");time=60;$("#sendCode").attr("class", "");}else {//每秒调用一次当前函数,使得time--$("#sendCode").text(time+"s后再次发送");time--;setTimeout("timeOutChangeStyle()", 1000);}}
(2) 整合短信服务

在阿里云网页购买试用的短信服务

gulimall-third-party中编写发送短信组件,其中hostpathappcode可以在配置文件中使用前缀spring.cloud.alicloud.sms进行配置

@Data
@ConfigurationProperties(prefix = "spring.cloud.alicloud.sms")
@Controller
public class SmsComponent {private String host;private String path;private String appcode;public void sendCode(String phone,String code) {
//        String host = "http://dingxin.market.alicloudapi.com";
//        String path = "/dx/sendSms";String method = "POST";
//        String appcode = "你自己的AppCode";Map<String, String> headers = new HashMap<String, String>();//最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105headers.put("Authorization", "APPCODE " + appcode);Map<String, String> querys = new HashMap<String, String>();querys.put("mobile",phone);querys.put("param", "code:"+code);querys.put("tpl_id", "TP1711063");Map<String, String> bodys = new HashMap<String, String>();try {/*** 重要提示如下:* HttpUtils请从* https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java* 下载** 相应的依赖请参照* https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml*/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();}}
}

编写controller,给别的服务提供远程调用发送验证码的接口

@Controller
@RequestMapping(value = "/sms")
public class SmsSendController {@Resourceprivate SmsComponent smsComponent;/*** 提供给别的服务进行调用* @param phone 电话号码* @param code 验证码* @return*/@ResponseBody@GetMapping(value = "/sendCode")public R sendCode(@RequestParam("phone") String phone, @RequestParam("code") String code) {//发送验证码smsComponent.sendCode(phone,code);System.out.println(phone+code);return R.ok();}
}
(3) 接口防刷

由于发送验证码的接口暴露,为了防止恶意攻击,我们不能随意让接口被调用。

  • 在redis中以phone-code将电话号码和验证码进行存储并将当前时间与code一起存储
    • 如果调用时以当前phone取出的v不为空且当前时间在存储时间的60s以内,说明60s内该号码已经调用过,返回错误信息
    • 60s以后再次调用,需要删除之前存储的phone-code
    • code存在一个过期时间,我们设置为10min,10min内验证该验证码有效
@GetMapping("/sms/sendCode")
@ResponseBody
public R sendCode(@RequestParam("phone")String phone) {//接口防刷,在redis中缓存phone-codeValueOperations<String, String> ops = redisTemplate.opsForValue();String prePhone = AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone;String v = ops.get(prePhone);if (!StringUtils.isEmpty(v)) {long pre = Long.parseLong(v.split("_")[1]);//如果存储的时间小于60s,说明60s内发送过验证码if (System.currentTimeMillis() - pre < 60000) {return R.error(BizCodeEnum.SMS_CODE_EXCEPTION.getCode(), BizCodeEnum.SMS_CODE_EXCEPTION.getMsg());}}//如果存在的话,删除之前的验证码redisTemplate.delete(prePhone);//获取到6位数字的验证码String code = String.valueOf((int)((Math.random() + 1) * 100000));//在redis中进行存储并设置过期时间ops.set(prePhone,code+"_"+System.currentTimeMillis(),10, TimeUnit.MINUTES);thirdPartFeignService.sendCode(phone, code);return R.ok();
}
(4) 注册接口编写

gulimall-auth-server服务中编写注册的主体逻辑

  • 若JSR303校验未通过,则通过BindingResult封装错误信息,并重定向至注册页面
  • 若通过JSR303校验,则需要从redis中取值判断验证码是否正确,正确的话通过会员服务注册
  • 会员服务调用成功则重定向至登录页,否则封装远程服务返回的错误信息返回至注册页面

注: RedirectAttributes可以通过session保存信息并在重定向的时候携带过去

 @PostMapping("/register")public String register(@Valid UserRegisterVo registerVo, BindingResult result, RedirectAttributes attributes) {//1.判断校验是否通过Map<String, String> errors = new HashMap<>();if (result.hasErrors()){//1.1 如果校验不通过,则封装校验结果result.getFieldErrors().forEach(item->{errors.put(item.getField(), item.getDefaultMessage());//1.2 将错误信息封装到session中attributes.addFlashAttribute("errors", errors);});//1.2 重定向到注册页return "redirect:http://auth.gulimall.com/reg.html";}else {//2.若JSR303校验通过//判断验证码是否正确String code = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + registerVo.getPhone());//2.1 如果对应手机的验证码不为空且与提交上的相等-》验证码正确if (!StringUtils.isEmpty(code) && registerVo.getCode().equals(code.split("_")[0])) {//2.1.1 使得验证后的验证码失效redisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX + registerVo.getPhone());//2.1.2 远程调用会员服务注册R r = memberFeignService.register(registerVo);if (r.getCode() == 0) {//调用成功,重定向登录页return "redirect:http://auth.gulimall.com/login.html";}else {//调用失败,返回注册页并显示错误信息String msg = (String) r.get("msg");errors.put("msg", msg);attributes.addFlashAttribute("errors", errors);return "redirect:http://auth.gulimall.com/reg.html";}}else {//2.2 验证码错误errors.put("code", "验证码错误");attributes.addFlashAttribute("errors", errors);return "redirect:http://auth.gulimall.com/reg.html";}}}

通过gulimall-member会员服务注册逻辑

  • 通过异常机制判断当前注册会员名和电话号码是否已经注册,如果已经注册,则抛出对应的自定义异常,并在返回时封装对应的错误信息
  • 如果没有注册,则封装传递过来的会员信息,并设置默认的会员等级、创建时间
 @RequestMapping("/register")public R register(@RequestBody MemberRegisterVo registerVo) {try {memberService.register(registerVo);//异常机制:通过捕获对应的自定义异常判断出现何种错误并封装错误信息} catch (UserExistException userException) {return R.error(BizCodeEnum.USER_EXIST_EXCEPTION.getCode(), BizCodeEnum.USER_EXIST_EXCEPTION.getMsg());} catch (PhoneNumExistException phoneException) {return R.error(BizCodeEnum.PHONE_EXIST_EXCEPTION.getCode(), BizCodeEnum.PHONE_EXIST_EXCEPTION.getMsg());}return R.ok();}
public void register(MemberRegisterVo registerVo) {//1 检查电话号是否唯一checkPhoneUnique(registerVo.getPhone());//2 检查用户名是否唯一checkUserNameUnique(registerVo.getUserName());//3 该用户信息唯一,进行插入MemberEntity entity = new MemberEntity();//3.1 保存基本信息entity.setUsername(registerVo.getUserName());entity.setMobile(registerVo.getPhone());entity.setCreateTime(new Date());//3.2 使用加密保存密码BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();String encodePassword = passwordEncoder.encode(registerVo.getPassword());entity.setPassword(encodePassword);//3.3 设置会员默认等级//3.3.1 找到会员默认登记MemberLevelEntity defaultLevel = memberLevelService.getOne(new QueryWrapper<MemberLevelEntity>().eq("default_status", 1));//3.3.2 设置会员等级为默认entity.setLevelId(defaultLevel.getId());// 4 保存用户信息this.save(entity);
}private void checkUserNameUnique(String userName) {Integer count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("username", userName));if (count > 0) {throw new UserExistException();}
}private void checkPhoneUnique(String phone) {Integer count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("mobile", phone));if (count > 0) {throw new PhoneNumExistException();}
}

3. 用户名密码登录

gulimall-auth-server模块中的主体逻辑

  • 通过会员服务远程调用登录接口
    • 如果调用成功,重定向至首页
    • 如果调用失败,则封装错误信息并携带错误信息重定向至登录页
@RequestMapping("/login")
public String login(UserLoginVo vo,RedirectAttributes attributes){R r = memberFeignService.login(vo);if (r.getCode() == 0) {return "redirect:http://gulimall.com/";}else {String msg = (String) r.get("msg");Map<String, String> errors = new HashMap<>();errors.put("msg", msg);attributes.addFlashAttribute("errors", errors);return "redirect:http://auth.gulimall.com/login.html";}
}

gulimall-member模块中完成登录

  • 当数据库中含有以当前登录名为用户名或电话号且密码匹配时,验证通过,返回查询到的实体
  • 否则返回null,并在controller返回用户名或密码错误
@RequestMapping("/login")
public R login(@RequestBody MemberLoginVo loginVo) {MemberEntity entity=memberService.login(loginVo);if (entity!=null){return R.ok();}else {return R.error(BizCodeEnum.LOGINACCT_PASSWORD_EXCEPTION.getCode(), BizCodeEnum.LOGINACCT_PASSWORD_EXCEPTION.getMsg());}
}@Overridepublic MemberEntity login(MemberLoginVo loginVo) {String loginAccount = loginVo.getLoginAccount();//以用户名或电话号登录的进行查询MemberEntity entity = this.getOne(new QueryWrapper<MemberEntity>().eq("username", loginAccount).or().eq("mobile", loginAccount));if (entity!=null){BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();boolean matches = bCryptPasswordEncoder.matches(loginVo.getPassword(), entity.getPassword());if (matches){entity.setPassword("");return entity;}}return null;}

4. 社交登录

(1) oauth2.0

在这里插入图片描述

(2) 在微博开放平台创建应用

在这里插入图片描述

(3) 在登录页引导用户至授权页
GET
https://api.weibo.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI
  • client_id: 创建网站应用时的app key
  • YOUR_REGISTERED_REDIRECT_URI: 认证完成后的跳转链接(需要和平台高级设置一致)

在这里插入图片描述

如果用户同意授权,页面跳转至 YOUR_REGISTERED_REDIRECT_URI/?code=CODE

code是我们用来换取令牌的参数

(4) 换取token
POST
https://api.weibo.com/oauth2/access_token?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&grant_type=authorization_code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI&code=CODE
  • client_id: 创建网站应用时的app key
  • client_secret: 创建网站应用时的app secret
  • YOUR_REGISTERED_REDIRECT_URI: 认证完成后的跳转链接(需要和平台高级设置一致)
  • code:换取令牌的认证码

返回数据如下

在这里插入图片描述

(5) 获取用户信息

https://open.weibo.com/wiki/2/users/show

结果返回json

(6) 代码编写

认证接口

  • 通过HttpUtils发送请求获取token,并将token等信息交给member服务进行社交登录
  • 若获取token失败或远程调用服务失败,则封装错误信息重新转回登录页
@Controller
public class OauthController {@Autowiredprivate MemberFeignService memberFeignService;@RequestMapping("/oauth2.0/weibo/success")public String authorize(String code, RedirectAttributes attributes) throws Exception {//1. 使用code换取token,换取成功则继续2,否则重定向至登录页Map<String, String> query = new HashMap<>();query.put("client_id", "2144***074");query.put("client_secret", "ff63a0d8d5*****29a19492817316ab");query.put("grant_type", "authorization_code");query.put("redirect_uri", "http://auth.gulimall.com/oauth2.0/weibo/success");query.put("code", code);//发送post请求换取tokenHttpResponse response = HttpUtils.doPost("https://api.weibo.com", "/oauth2/access_token", "post", new HashMap<String, String>(), query, new HashMap<String, String>());Map<String, String> errors = new HashMap<>();if (response.getStatusLine().getStatusCode() == 200) {//2. 调用member远程接口进行oauth登录,登录成功则转发至首页并携带返回用户信息,否则转发至登录页String json = EntityUtils.toString(response.getEntity());SocialUser socialUser = JSON.parseObject(json, new TypeReference<SocialUser>() {});R login = memberFeignService.login(socialUser);//2.1 远程调用成功,返回首页并携带用户信息if (login.getCode() == 0) {String jsonString = JSON.toJSONString(login.get("memberEntity"));MemberResponseVo memberResponseVo = JSON.parseObject(jsonString, new TypeReference<MemberResponseVo>() {});attributes.addFlashAttribute("user", memberResponseVo);return "redirect:http://gulimall.com";}else {//2.2 否则返回登录页errors.put("msg", "登录失败,请重试");attributes.addFlashAttribute("errors", errors);return "redirect:http://auth.gulimall.com/login.html";}}else {errors.put("msg", "获得第三方授权失败,请重试");attributes.addFlashAttribute("errors", errors);return "redirect:http://auth.gulimall.com/login.html";}}

登录接口

  • 登录包含两种流程,实际上包括了注册和登录
  • 如果之前未使用该社交账号登录,则使用token调用开放api获取社交账号相关信息,注册并将结果返回
  • 如果之前已经使用该社交账号登录,则更新token并将结果返回
@RequestMapping("/oauth2/login")
public R login(@RequestBody SocialUser socialUser) {MemberEntity entity=memberService.login(socialUser);if (entity!=null){return R.ok().put("memberEntity",entity);}else {return R.error();}
}@Overridepublic MemberEntity login(SocialUser socialUser){MemberEntity uid = this.getOne(new QueryWrapper<MemberEntity>().eq("uid", socialUser.getUid()));//1 如果之前未登陆过,则查询其社交信息进行注册if (uid == null) {Map<String, String> query = new HashMap<>();query.put("access_token",socialUser.getAccess_token());query.put("uid", socialUser.getUid());//调用微博api接口获取用户信息String json = null;try {HttpResponse response = HttpUtils.doGet("https://api.weibo.com", "/2/users/show.json", "get", new HashMap<>(), query);json = EntityUtils.toString(response.getEntity());} catch (Exception e) {e.printStackTrace();}JSONObject jsonObject = JSON.parseObject(json);//获得昵称,性别,头像String name = jsonObject.getString("name");String gender = jsonObject.getString("gender");String profile_image_url = jsonObject.getString("profile_image_url");//封装用户信息并保存uid = new MemberEntity();MemberLevelEntity defaultLevel = memberLevelService.getOne(new QueryWrapper<MemberLevelEntity>().eq("default_status", 1));uid.setLevelId(defaultLevel.getId());uid.setNickname(name);uid.setGender("m".equals(gender)?0:1);uid.setHeader(profile_image_url);uid.setAccessToken(socialUser.getAccess_token());uid.setUid(socialUser.getUid());uid.setExpiresIn(socialUser.getExpires_in());this.save(uid);}else {//2 否则更新令牌等信息并返回uid.setAccessToken(socialUser.getAccess_token());uid.setUid(socialUser.getUid());uid.setExpiresIn(socialUser.getExpires_in());this.updateById(uid);}return uid;}

5. SpringSession

(1) session 原理

jsessionid相当于银行卡,存在服务器的session相当于存储的现金,每次通过jsessionid取出保存的数据

问题:但是正常情况下session不可跨域,它有自己的作用范围

在这里插入图片描述

(2) 分布式下session共享问题

在这里插入图片描述

(3) 解决方案
1) session复制

在这里插入图片描述

2) 客户端存储

在这里插入图片描述

3) hash一致性

在这里插入图片描述

4) 统一存储

在这里插入图片描述

(4) SpringSession整合redis

通过SpringSession修改session的作用域

在这里插入图片描述

1) 环境搭建

导入依赖

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

修改配置

spring:redis:host: 192.168.56.102session:store-type: redis

添加注解

@EnableRedisHttpSession
public class GulimallAuthServerApplication {
2) 自定义配置
  • 由于默认使用jdk进行序列化,通过导入RedisSerializer修改为json序列化

  • 并且通过修改CookieSerializer扩大session的作用域至**.gulimall.com

@Configuration
public class GulimallSessionConfig {@Beanpublic RedisSerializer<Object> springSessionDefaultRedisSerializer() {return new GenericJackson2JsonRedisSerializer();}@Beanpublic CookieSerializer cookieSerializer() {DefaultCookieSerializer serializer = new DefaultCookieSerializer();serializer.setCookieName("GULISESSIONID");serializer.setDomainName("gulimall.com");return serializer;}
}
(5) SpringSession核心原理 - 装饰者模式
  • 原生的获取session时是通过HttpServletRequest获取的
  • 这里对request进行包装,并且重写了包装request的getSession()方法
@Override
protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);//对原生的request、response进行包装SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response, this.servletContext);SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest, response);try {filterChain.doFilter(wrappedRequest, wrappedResponse);}finally {wrappedRequest.commitSession();}
}

购物车

1. 数据模型分析

(1) 数据存储

购物车是一个读多写多的场景,因此放入数据库并不合适,但购物车又是需要持久化,因此这里我们选用redis存储购物车数据。

(2) 数据结构

在这里插入图片描述

一个购物车是由各个购物项组成的,但是我们用List进行存储并不合适,因为使用List查找某个购物项时需要挨个遍历每个购物项,会造成大量时间损耗,为保证查找速度,我们使用hash进行存储

在这里插入图片描述

(3) VO编写

购物项vo

在这里插入图片描述

public class CartItemVo {private Long skuId;//是否选中private Boolean check = true;//标题private String title;//图片private String image;//商品套餐属性private List<String> skuAttrValues;//价格private BigDecimal price;//数量private Integer count;//总价private BigDecimal totalPrice;/*** 当前购物车项总价等于单价x数量* @return*/public BigDecimal getTotalPrice() {return price.multiply(new BigDecimal(count));}public void setTotalPrice(BigDecimal totalPrice) {this.totalPrice = totalPrice;}

购物车vo

在这里插入图片描述

public class CartVo {/*** 购物车子项信息*/List
http://www.zhongyajixie.com/news/24426.html

相关文章:

  • wordpress页面权限宁波seo外包优化公司
  • 高大上的平面设计网站营业推广方式
  • 上海网站建设模板网站建设是干什么的
  • 做网站需要了解什么如何推广一个产品
  • 做网站维护要多少钱一年东莞网站建设最牛
  • 做网站 前端传统营销方式有哪些
  • 建站点百度客服怎么转人工
  • 做短视频网站用哪家cms百度秒收录软件工具
  • 中国招标与采购网官网优化设计六年级上册数学答案
  • 房地产网站做编辑刚刚入行微信客户管理系统平台
  • 无锡企业网站的建设最佳磁力引擎吧
  • 做外贸怎样免费登录外国网站互联网推广中心
  • 让人做网站需要注意什么企业建站
  • 在线制作logo图标免费制作seo研究中心qq群
  • 陕西省西安市建设局网站seo都用在哪些网站
  • 松江新城做网站公司怎么找网站
  • 汽车网站建设需要多少钱网络营销的优势包括
  • 个人做旅游网站怎样客源引流推广
  • 学做ps的网站拓客引流推广
  • 用javaee做的网站模板商丘seo优化
  • 扬州做网站的公司哪个好搜索引擎优化seo多少钱
  • 网站不备案行吗沈阳seo关键字优化
  • 网站 只做程序员长沙网站seo技术厂家
  • 正宗营销型网站建设交友平台
  • 谷歌独立站建站得多少钱青岛专业网站制作
  • 用什么软件做网站原型企点qq
  • 做网站需要会编程语言吗国内优秀网站案例
  • 装饰公司网站建设方案seo是什么职位的简称
  • 免费电影在线观看完整版正规seo多少钱
  • wordpress数据量大网站访问河南网站建设优化技术