註冊包括郵箱註冊和手機號註冊,兩種註冊方法業務都相同,只是激活方式不一樣javascript
用戶在頁面輸入郵箱/手機號,點擊獲取驗證碼,就會向後臺發送post請求php
後端接收到前端請求,發送驗證碼html
向後臺發送請求,帶有註冊的信息前端
後端響應請求獲取請求攜帶的參數,此時咱們可使用一個臨時對象接受參數(方便定義添加屬性)java
首先第一件事就是進行校驗ios
校驗數據是否爲空,若是爲空,拋出異常提示用戶輸入完整的信息web
校驗手機號是否已被註冊,經過前端傳入的用戶輸入的手機號到數據庫用戶表進行查詢,若是存在,拋出異常提示該手機已註冊redis
校驗兩次密碼是否相同spring
校驗驗證碼,經過用戶手機號和固定常量從redis中獲取剛纔發送的驗證碼sql
校驗成功以後,將臨時對象轉化爲登錄信息對象,保存登錄信息對象,就有了id
注意咱們在保存密碼時爲了安全性,須要對密碼進行加密,咱們是經過鹽值進行加密
經過隨機數獲取鹽值,並保存到登陸信息中
加密方式MD5:輸入任意長度的信息,通過處理,輸出都是128位的信息值,沒法看到明文
不一樣的輸入對應的輸出必定不一樣,保證惟一性
計算速度快,加密速度快
調用**MD5工具類**的方法使用密碼+鹽值進行加密並設置保存 注意有順序次數的問題
須要注意的是咱們的登陸信息包含前臺普通用戶和後臺用戶,爲了區分,咱們須要設置帳戶類型(0表明後臺用戶,1表明前臺普通用戶)
將登錄信息對象轉化爲user前端用戶對象並保存,這樣咱們將關聯了前端用戶和登錄信息對象
沒有問題就返回AjaxResult-success給前端
前端接收返回值,判斷,若是註冊成功,跳轉到前端首頁,若是不成功,就將錯誤異常展現到頁面
用戶是一張表,管理員是一張表,而後咱們還作了一個登陸表,登陸表裏面是全部用戶和管理員的登陸帳號和密碼,由於管理員也能夠登陸咱們的網站享受服務,因此有可能一個手機或郵箱既是用戶又是管理員。爲了區分這種登陸帳號究竟是要登陸到後臺仍是網站,因此咱們登陸表裏面有一個type字段,區分該帳號究竟是用戶仍是管理員
當用戶輸入信息點擊登陸時發送post登陸請求,並攜帶了登陸信息
後臺響應請求使用臨時對象接收參數
校驗登陸信息
校驗登陸信息正確以後,經過UUID產生隨機的token值
將token值和對應的的登陸信息對象保存到redis內存中
將token做爲key,登陸信息對象做爲value,返回一個map給前端
登陸成功,後端會返回map格式的token值,將token值保存到**localStorage**中
跳轉到前端首頁
axios前端前置攔截器:當前端每次發起請求時,都要將token值放入請求頭中,每個請求都須要token,因此咱們能夠配置一個前置攔截器,爲全部請求頭添加token
後端攔截器:後端接收前端的訪問請求,判斷有沒有token,經過token查詢數據庫中是否有對應的登陸信息
axios前端後置攔截器:判斷後端攔截器返回的狀態,若是爲未登陸狀態,則跳轉到登陸頁面,若是已登陸狀態,則跳轉到請求訪問的頁面 **問題?**返回什麼結果如何跳轉到請求頁面
package cn.itsource.pethome.basic.util;import java.util.ArrayList;import java.util.List;import java.util.Random;/** * @author yaohuaipeng * @date 2018/10/26-16:16 */public class StrUtils {/** * 把逗號分隔的字符串轉換字符串數組 * * @param str * @return */public static String[] splitStr2StrArr(String str,String split) {if (str != null && !str.equals("")) {return str.split(split); }return null; }/** * 把逗號分隔字符串轉換List的Long * * @param str * @return */public static List<Long> splitStr2LongArr(String str) { String[] strings = splitStr2StrArr(str,",");if (strings == null) return null; List<Long> result = new ArrayList<>();for (String string : strings) { result.add(Long.parseLong(string)); }return result; }/** * 把逗號分隔字符串轉換List的Long * * @param str * @return */public static List<Long> splitStr2LongArr(String str,String split) { String[] strings = splitStr2StrArr(str,split);if (strings == null) return null; List<Long> result = new ArrayList<>();for (String string : strings) { result.add(Long.parseLong(string)); }return result; }public static String getRandomString(int length) { String str = "01234567892342343243"; Random random = new Random(); StringBuffer sb = new StringBuffer();for (int i = 0; i < length; i++) {int number = random.nextInt(10); sb.append(str.charAt(number)); }return sb.toString(); }public static String getComplexRandomString(int length) { String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer();for (int i = 0; i < length; i++) {int number = random.nextInt(62); sb.append(str.charAt(number)); }return sb.toString(); }public static void main(String[] args) { String s = getComplexRandomString(4); System.out.println(s); }public static String convertPropertiesToHtml(String properties){//1:容量:6:32GB_4:樣式:12:塑料殼StringBuilder sBuilder = new StringBuilder(); String[] propArr = properties.split("_");for (String props : propArr) { String[] valueArr = props.split(":"); sBuilder.append(valueArr[1]).append(":").append(valueArr[3]).append("<br>"); }return sBuilder.toString(); } }複製代碼
數據庫分類
NoSQL非關係型數據庫:存儲數據都是沒有結構的(沒有表的概念),而且存儲數據都是以key-value的的方式存儲,都是把數據存儲到內存或者磁盤上
RDBSM關係型數據庫:是有行和列組成的二維表,存儲數據是有必定格式的,都是把數據存儲到磁盤上
區別:
Redis是一個高性能的開源的,c語言寫的NoSQL,數據保存在內存/磁盤中。
Redis是以key-value形式存儲,不必定遵循傳統數據庫的一些基本要求,好比不遵循sql標準,事務,表結構等,redis嚴格說來不是一個數據庫,應該是一種數據結構化存儲方法的集合
存儲數據都是以字符串的形式進行存儲,把存儲的字符串按必定的規則進行擺放(規則:String list set zset Hash)
綠色版的在cmd終端啓動服務 redis-server.exe redis.window.config
而後咱們在springboot項目中使用redis
導包
<!--對redis的支持--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>複製代碼
配置文件application.properties
# redis 屬性配置## redis數據庫索引(默認爲0)spring.redis.database=0## redis服務器地址spring.redis.host=localhost## redis服務器鏈接端口spring.redis.port=6379## redis服務器鏈接密碼(默認爲空)spring.redis.password=123456## 鏈接池最大鏈接數(使用負值表示沒有限制)spring.redis.jedis.pool.max-active=8## 鏈接池中的最大空閒鏈接spring.redis.jedis.pool.max-idle=8## 鏈接池最大阻塞等待時間(使用負值表示沒有限制)spring.redis.jedis.pool.max-wait=-1ms## 鏈接池中的最小空閒鏈接spring.redis.jedis.pool.min-idle=0複製代碼
保存驗證碼到redis(咱們爲驗證碼設置對應的全局常量做爲標識,並設置了當前的毫秒值,方便驗證碼過時問題)
/package cn.itsource.pethome.basic.service.impl; import cn.itsource.pethome.basic.constant.PetHomeConstant;import cn.itsource.pethome.basic.service.IVerificationService;import cn.itsource.pethome.basic.util.SendMsgUtil;import cn.itsource.pethome.basic.util.StrUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;import org.springframework.util.StringUtils; import java.util.concurrent.TimeUnit; @Service@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)public class VerificationCodeServiceImpl implements IVerificationService { @Autowiredprivate RedisTemplate redisTemplate; /** * 驗證碼特色: * 1.隨機產生4位 * 2.有效期5分===》redis * 3.區分驗證碼類型 * * 市場上常見的驗證碼: * 1.有效期是5分鐘,若是在60s之內我連續發了2次, * 1種方案是提示前端用戶,不能重複發送, * 若是超過了60s,但沒有超過5分鐘,我就不產生新的驗證碼,仍是用第一次產生的驗證碼 * 若是超過了5分鐘, 就產生全新的驗證碼 * * @param phone 手機號碼 */@Overridepublic void sendRegisterMobileCode(String phone) {//手機號發送手機驗證碼sendMobileCode(phone,PetHomeConstant.REGISTER_VERIFICATION_CODE); } @Overridepublic void sendBinderMobileCode(String phone) {//綁定用戶發送手機驗證碼sendMobileCode(phone,PetHomeConstant.BINDER_VERIFICATION_CODE); } private void sendMobileCode(String phone,String type){//1.產生隨機4位的驗證碼 JK82String code = StrUtils.getComplexRandomString(4);//經過手機號碼在redis中獲取對應的驗證碼 JK82:21312433542423String codeValue = (String) redisTemplate.opsForValue().get(type+":"+phone);if(!StringUtils.isEmpty(codeValue)){//獲取第一次的毫秒時間Long beginTimer = Long.valueOf(codeValue.split(":")[1]);if(System.currentTimeMillis()-beginTimer<60*1000){throw new RuntimeException("一分鐘之內不能重複獲取驗證碼!!!"); }//若是沒有走拋異常就證實驗證碼依然在5分鐘之內,可是超過了1分鐘code = codeValue.split(":")[0]; }//2.把驗證碼存儲到redis中,而且有效期是5分鐘redisTemplate.opsForValue().set(type+":"+phone, code+":"+System.currentTimeMillis(),5, TimeUnit.MINUTES); String content = "尊敬的用戶,你的驗證碼爲:"+code+",請在5分鐘之內完成驗證操做!!"; System.out.println(content); // 發送驗證碼 // SendMsgUtil.sendMsg(phone, content);} } 複製代碼
手機發送驗證碼的包
<!-- https://mvnrepository.com/artifact/commons-httpclient/commons-httpclient --><dependency><groupId>commons-httpclient</groupId><artifactId>commons-httpclient</artifactId><version>3.1</version></dependency>複製代碼
發送驗證碼的工具類
package cn.itsource.pethome.basic.util;import org.apache.commons.httpclient.Header;import org.apache.commons.httpclient.HttpClient;import org.apache.commons.httpclient.NameValuePair;import org.apache.commons.httpclient.methods.PostMethod;import java.io.IOException;public class SendMsgUtil {//本站用戶名public static final String UID = "huangxuyang";public static final String KEY = "d41d8cd98f00b204e980";/** * 發送郵件 * @param phone 手機號碼 * @param content 短信內容 */public static void sendMsg(String phone,String content){ PostMethod post = null;try {//建立客戶端HttpClient client = new HttpClient();//發送post請求post = new PostMethod("http://utf8.api.smschinese.cn");//添加請求頭信息post.addRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=utf8");//在頭文件中設置轉碼//設置請求的基本信息NameValuePair[] data ={ new NameValuePair("Uid", UID),new NameValuePair("Key", KEY),new NameValuePair("smsMob",phone),new NameValuePair("smsText",content)};//設置請求體post.setRequestBody(data);//開始調用client.executeMethod(post);//如下代碼沒什麼用了,就是返回響應狀態而已Header[] headers = post.getResponseHeaders();int statusCode = post.getStatusCode(); System.out.println("statusCode:"+statusCode);for(Header h : headers) { System.out.println(h.toString()); } String result = new String(post.getResponseBodyAsString().getBytes("gbk")); System.out.println(result); //打印返回消息狀態} catch (IOException e) { e.printStackTrace(); } finally {if(post!=null){ post.releaseConnection(); } } } }複製代碼
發送郵件的配置
# 設置郵箱主機(服務商)spring.mail.host=smtp.qq.com# 設置用戶名spring.mail.username=798477672@qq.com# 設置密碼,該處的密碼是QQ郵箱開啓SMTP的受權碼而非QQ密碼spring.mail.password=eglwfhofzaoubche# 必須進行受權認證,它的目的就是阻止他人任意亂髮郵件spring.mail.properties.mail.smtp.auth=true#SMTP加密方式:鏈接到一個TLS保護鏈接spring.mail.properties.mail.smtp.starttls.enable=truespring.mail.properties.mail.smtp.starttls.required=true複製代碼
發送郵件的實現
package cn.itsource.pethome;import cn.itsource.pethome.App;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.mail.SimpleMailMessage;import org.springframework.mail.javamail.JavaMailSender;import org.springframework.mail.javamail.MimeMessageHelper;import org.springframework.test.context.junit4.SpringJUnit4Cla***unner;import javax.mail.MessagingException;import javax.mail.internet.MimeMessage;import java.io.File;@SpringBootTest(classes = App.class)@RunWith(SpringJUnit4Cla***unner.class)public class SimpleEmailTest {@Autowiredprivate JavaMailSender javaMailSender;@Testpublic void test(){//首先建立普通郵件SimpleMailMessage message = new SimpleMailMessage();//設置普通郵件的發件人message.setFrom("798477672@qq.com");//設置收件人message.setTo("798477672@qq.com");//設置郵件標題message.setSubject("入學通知");//設置文件內容message.setText("恭喜你,我校對於你的表現十分滿意,特招你成爲咱們的學生——新東方技術學院");//發送郵件javaMailSender.send(message); }@Testpublic void test2() throws MessagingException {//建立複雜文件對象MimeMessage mimeMessage = javaMailSender.createMimeMessage();//獲取複雜郵件的工具類MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true, "utf-8");//設置發件人messageHelper.setFrom("798477672@qq.com");//設置文件標題messageHelper.setSubject("天上人間邀請函");//設置文件內容messageHelper.setText("<h1>天上人間</h1>"+"<img src='https://pic.feizl.com/upload2007/allimg/170628/1KK2IF-14.jpg' />" ,true);//設置附件messageHelper.addAttachment("1.jpg", new File("D:\\圖片壁紙\\壁紙\\1.jpg"));//設置收件人messageHelper.setTo("798477672@qq.com");//發送郵件javaMailSender.send(mimeMessage); } }複製代碼
MD5加密工具類
package cn.itsource.pethome.basic.util;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;public class MD5Utils {/** * 加密 * @param context */public static String encrypByMd5(String context) {try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(context.getBytes());//update處理 byte [] encryContext = md.digest();//調用該方法完成計算 int i; StringBuffer buf = new StringBuffer(""); for (int offset = 0; offset < encryContext.length; offset++) {//作相應的轉化(十六進制) i = encryContext[offset]; if (i < 0) i += 256; if (i < 16) buf.append("0"); buf.append(Integer.toHexString(i)); } return buf.toString(); } catch (NoSuchAlgorithmException e) {// TODO Auto-generated catch block e.printStackTrace();return null; } }public static void main(String[] args) {//加密System.out.println(MD5Utils.encrypByMd5("1")+"37XFtqiwKz");//加密加鹽 查詢用戶時,除了查到加密密碼外,還能查到顏值。 把輸入密碼+鹽值加密和數據庫存放密碼比對就OKSystem.out.println(MD5Utils.encrypByMd5("123456"+ StrUtils.getComplexRandomString(32))); System.out.println(MD5Utils.encrypByMd5("123456"+ StrUtils.getComplexRandomString(32))); System.out.println(MD5Utils.encrypByMd5("123456"+ StrUtils.getComplexRandomString(32))); } }複製代碼
storage的使用
<script type="text/javascript"> new Vue({ el: "#app", mounted(){ //獲取url?傳遞的值 let param = getParam() //發送axios請求,經過受權碼獲取token this.$http.post("/wechat/getToken",param).then(res=>{ console.debug(res.data); let {success, msg, resultobj} = res.data; //若是成功,而且openid有值 if (success && resultobj.openid) { location.href = "binder.html?openid=" + resultobj.openid; } else if (success && resultobj.token && resultobj.loginUser) { //將token'值保存到localStorage中 localStorage.setItem("token", resultobj.token); localStorage.setItem("loginUser", JSON.stringify(resultobj.loginUser)); location.href = "/index.html"; } }) } }) </script>複製代碼
保存密碼加密
package cn.itsource.pethome.user.service.impl;import cn.itsource.pethome.basic.constant.PetHomeConstant;import cn.itsource.pethome.org.domain.Employee;import cn.itsource.pethome.user.domain.Logininfo;import cn.itsource.pethome.user.domain.User;import cn.itsource.pethome.user.domain.dto.UserDto;import cn.itsource.pethome.user.mapper.LogininfoMapper;import cn.itsource.pethome.user.mapper.UserMapper;import cn.itsource.pethome.user.service.IUserService;import cn.itsource.pethome.basic.util.MD5Utils;import cn.itsource.pethome.basic.util.StrUtils;import org.springframework.beans.BeanUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;import org.springframework.util.StringUtils;@Service@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)public class UserServiceImpl implements IUserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate LogininfoMapper logininfoMapper; @Autowiredprivate RedisTemplate redisTemplate;/** * 註冊用戶 * @param userDto */@Overridepublic void register(UserDto userDto) {//校驗傳入的臨時數據對象進行checkUserDto(userDto);//將臨時數據轉爲登錄信息Logininfo logininfo = userDto2Logininfo(userDto);//保存登錄信息logininfoMapper.save(logininfo);//將登錄信息logininfo對象轉化爲user對象User user = Logininfo2User(logininfo);//保存user對象userMapper.save(user); }/** * 將logininfo登錄信息對象轉化user對象 * @param logininfo * @return */private User Logininfo2User(Logininfo logininfo) { User user = new User();//使用方法拷貝對象,只拷貝字段相同的屬性BeanUtils.copyProperties(logininfo, user);//設置激活狀態user.setState(PetHomeConstant.STATEOK);//設置登陸信息user.setLogininfo(logininfo);return user; }/** * 將臨時對象數據轉爲爲登錄信息 * @param userDto * @return */private Logininfo userDto2Logininfo(UserDto userDto) { Logininfo logininfo = new Logininfo();//設置用戶名logininfo.setUsername(userDto.getPhone());//設置手機號logininfo.setPhone(userDto.getPhone());//設置鹽值logininfo.setSalt(StrUtils.getComplexRandomString(10));//設置密碼(使用鹽值進行加密)logininfo.setPassword(MD5Utils.encrypByMd5(userDto.getPassword()+logininfo.getSalt()));//登陸的類型(是後端登陸仍是前端登陸) 0表明管理員,1表明普通用戶,true默認爲前端用戶logininfo.setType(true);//disable是否可用默認爲可用return logininfo; }/** * 校驗前端傳入的註冊信息 * @param userDto */public void checkUserDto(UserDto userDto) {//1.校驗前端傳入的註冊信息是否爲空if(StringUtils.isEmpty(userDto.getCode()) || StringUtils.isEmpty(userDto.getPhone()) || StringUtils.isEmpty(userDto.getPassword()) || StringUtils.isEmpty(userDto.getPasswordRepeat())){throw new RuntimeException("請輸入完整信息!"); }//2.校驗手機號是否已被註冊//查詢根據前端傳入的手機號與數據庫比對User user = userMapper.findonebyphone(userDto.getPhone());//若是能查到手機號對應的用戶,說明已被註冊if(user!=null){throw new RuntimeException("該手機號已被註冊"); }//3.校驗用戶輸入的兩次密碼是否一致if (!userDto.getPassword().equals(userDto.getPasswordRepeat())) {throw new RuntimeException("兩次輸出的密碼不一致!"); }//4.校驗驗證碼是否過時//從redis內存中獲取用戶對應的驗證碼的信息,此時得到的驗證碼格式爲 code+時間戳String codeValue = (String) redisTemplate.opsForValue().get(PetHomeConstant.REGISTER_VERIFICATION_CODE + ":" + userDto.getPhone());//判斷驗證碼是否過時if (StringUtils.isEmpty(codeValue)) {throw new RuntimeException("驗證碼已過時,請從新獲取"); }//獲取redis中的驗證碼String code = codeValue.split(":")[0];//若是內存中保存的驗證碼和前端傳入的驗證碼不一樣,說明驗證碼已過時System.out.println(code);if (!code.toLowerCase().equals(userDto.getCode().toLowerCase())) {throw new RuntimeException("輸入的驗證碼錯誤!"); } } }複製代碼
後端控制器
package cn.itsource.pethome.user.interceptor;import cn.itsource.pethome.user.domain.Logininfo;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;import java.util.concurrent.TimeUnit;/** * 配置後端攔截器,判斷當前請求是否有token,沒有則告訴前端,還沒有登陸,無權限訪問 */@Componentpublic class LoginInterceptor implements HandlerInterceptor {@Autowiredprivate RedisTemplate redisTemplate;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { System.out.println(request.getRequestURL());//獲取token的值String token = request.getHeader("token");if(StringUtils.isEmpty(token)){// 提示前端用戶,登陸超時,請從新登陸wirteError(response);return false; } Logininfo loginInfo = (Logininfo) redisTemplate.opsForValue().get(token);if(loginInfo == null){// 提示前端用戶,登陸超時,請從新登陸wirteError(response);return false; } redisTemplate.opsForValue().set(token, loginInfo, 30, TimeUnit.MINUTES);return true; }private void wirteError(HttpServletResponse response){try { response.setContentType("text/json;charset=utf-8"); PrintWriter writer = response.getWriter(); writer.write("{\"success\":false,\"msg\":\"noLogin\"}"); } catch (IOException e) { e.printStackTrace(); } } }複製代碼
axios前置後置攔截器
Vue.prototype.$http = axios;/*給全部的axios請求,添加請求前綴*/axios.defaults.baseURL="http://localhost"; Vue.prototype.dfsUrl="http://115.159.217.249:8888";/*axios前置攔截器*/axios.interceptors.request.use(config => {//攜帶tokenlet token = localStorage.getItem("token");if (token) {//在頭信息中添加tokenconfig.headers['token'] = token; }return config; }, error => {Promise.reject(error); });//動態獲取url地址?//axios後置攔截器axios.interceptors.response.use(result => {let {success, msg} = result.data;if(!success && msg === "noLogin"){//清空本地存儲localStorage.removeItem("token");localStorage.removeItem("loginUser"); router.push({ path: '/login' }); }return result; }, error => {Promise.reject(error); });複製代碼
微信獲取token的插件
package cn.itsource.pethome.basic.util;import org.apache.commons.httpclient.HttpClient;import org.apache.commons.httpclient.methods.GetMethod;import org.apache.commons.httpclient.params.HttpMethodParams;import java.io.IOException;/** * 使用httpclient組件發送http請求 * get:如今只用到get * post */public class HttpClientUtils {/** * 發送get請求 * @param url 請求地址 * @return 返回內容 json */public static String httpGet(String url){// 1 建立發起請求客戶端try { HttpClient client = new HttpClient();// 2 建立要發起請求-tetGetMethod getMethod = new GetMethod(url);// getMethod.addRequestHeader("Content-Type",// "application/x-www-form-urlencoded;charset=UTF-8");getMethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET,"utf8");// 3 經過客戶端傳入請求就能夠發起請求,獲取響應對象client.executeMethod(getMethod);// 4 提取響應json字符串返回String result = new String(getMethod.getResponseBodyAsString().getBytes("utf8"));return result; } catch (IOException e) { e.printStackTrace(); }return null; } }複製代碼
微信登錄的實現
package cn.itsource.pethome.user.service.impl;import cn.itsource.pethome.basic.constant.PetHomeConstant;import cn.itsource.pethome.basic.util.HttpClientUtils;import cn.itsource.pethome.basic.util.MD5Utils;import cn.itsource.pethome.basic.util.StrUtils;import cn.itsource.pethome.user.domain.Logininfo;import cn.itsource.pethome.user.domain.User;import cn.itsource.pethome.user.domain.WechatUser;import cn.itsource.pethome.user.domain.dto.UserDto;import cn.itsource.pethome.user.mapper.LogininfoMapper;import cn.itsource.pethome.user.mapper.UserMapper;import cn.itsource.pethome.user.mapper.WechatMapper;import cn.itsource.pethome.user.service.IWechatService;import com.alibaba.fastjson.JSONObject;import org.springframework.beans.BeanUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Service;import org.springframework.util.StringUtils;import java.util.HashMap;import java.util.Map;import java.util.UUID;@Servicepublic class WechatServerImpl implements IWechatService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate LogininfoMapper logininfoMapper;@Autowiredprivate WechatMapper wechatMapper;@Autowiredprivate RedisTemplate redisTemplate;/** * 判斷用戶是否綁定 * @param code 受權碼 * @return */@Overridepublic Map<String, Object> getToken(String code) {//首先根據code獲取token的url地址String tokenUrl = PetHomeConstant.TOKENURL.replace("APPID", PetHomeConstant.APPID) .replace("SECRET", PetHomeConstant.SECRET) .replace("CODE", code);//而後向url地址發送get請求獲取json字符串格式的String httpGet = HttpClientUtils.httpGet(tokenUrl);//將json字符串轉化爲json對象JSONObject jsonObject = JSONObject.parseObject(httpGet); System.out.println(jsonObject);//經過json對象獲取openid惟一標識String openid = jsonObject.getString("openid");//獲取tokenString token = jsonObject.getString("access_token");//獲取unionid,當用戶受權以後纔有的標識String unionid = (String) jsonObject.get("unionid");//經過token獲取用戶資源urlString userinfoUrl = PetHomeConstant.USERINFOURL.replace("ACCESS_TOKEN", token) .replace("OPENID", openid);//經過資源url獲取用戶信息String userinfo = HttpClientUtils.httpGet(userinfoUrl);//將用戶信息json字符串轉爲json對象JSONObject userObject = JSONObject.parseObject(userinfo); System.out.println(userObject);//獲取惟一標識String userOpenid = userObject.getString("openid");//經過用戶的惟一標識openid去數據庫查找對應的數據WechatUser wechatUser = wechatMapper.loadByopenid(userOpenid);//設置一個map,封裝數據返回給前端Map<String, Object> map = new HashMap<>();//若是查詢的用戶爲空,說明用戶尚未綁定登錄信息if (wechatUser == null) { WechatUser user = new WechatUser();//設置惟一標識user.setOpenid(userOpenid);//設置用戶名user.setNickname(userObject.getString("nickname"));//設置性別user.setSex(userObject.getBoolean("sex"));//設置地址user.setAddress(userObject.getString("province")+userObject.getString("city"));//設置頭像user.setHeadimgurl(userObject.getString("headimgurl"));//設置受權後惟一標識user.setUnionid(userObject.getString("unionid"));//保存當前用戶wechatMapper.save(user);//將用戶的惟一標識響應前端,跳轉到受權頁面map.put("openid", openid);return map; }else{//若是user不爲空,判斷用戶是否綁定登錄信息Logininfo logininfo = wechatUser.getLogininfo(); System.out.println(logininfo+"--------------------------------------1");if (logininfo == null) {//響應給前端跳轉到綁定頁面map.put("openid", openid);return map; }else{//不爲空,表明已經綁定了,直接登錄System.out.println(logininfo+"----------------------------------------2");//建立tokenString token1 = UUID.randomUUID().toString();//將token放入內存中redisTemplate.opsForValue().set(token1, logininfo);//將token和用戶信息返回map.put("token", token1); map.put("loginUser", logininfo);return map; } } }/** * 進行綁定跳轉 * @param userDto * @return */@Overridepublic Map<String, Object> binder(UserDto userDto) {//首先校驗前端數據checkDto(userDto);//設置用戶名,方便在登錄信息中查詢該用戶對象userDto.setUsername(userDto.getPhone());//建立map集合,封裝登陸信息和token信息Map<String, Object> map = new HashMap<>();//根據用戶名查詢登錄信息Logininfo logininfo = logininfoMapper.loadByUserDto(userDto);if (logininfo == null) {//登錄信息爲空 logininfo = new Logininfo();//將用戶信息轉爲登錄信息logininfo =userDto2logininfo(userDto);//保存登錄信息logininfoMapper.save(logininfo);//將登錄信息轉爲user對象信息User user = logiinfo2User(logininfo);//保存user信息userMapper.save(user); }//根據查詢出來的登錄信息,將微信掃描碼的用戶信息與登錄信息關聯wechatMapper.binder(logininfo.getId(), userDto.getOpenid());//而後建立token,返回前端,直接登錄//建立tokenString token1 = UUID.randomUUID().toString();//將token放入內存中redisTemplate.opsForValue().set("token", token1);//將token和用戶信息返回map.put("token", token1); map.put("loginUser", logininfo);return map; }/** * 將登錄信息轉爲user信息 * @param logininfo */private User logiinfo2User(Logininfo logininfo) { User user = new User(); BeanUtils.copyProperties(logininfo, user); user.setState(PetHomeConstant.STATEOK); user.setLogininfo(logininfo);return user; }/** * 將臨時信息轉爲登錄信息 * @param userDto * @return */private Logininfo userDto2logininfo(UserDto userDto) { Logininfo logininfo = new Logininfo(); logininfo.setUsername(userDto.getPhone()); logininfo.setPhone(userDto.getPhone()); logininfo.setType(userDto.getType()); logininfo.setSalt(StrUtils.getComplexRandomString(10)); logininfo.setPassword(MD5Utils.encrypByMd5(logininfo.getPhone()+logininfo.getSalt()));return logininfo; }private void checkDto(UserDto userDto) {//校驗輸入信息完整if(StringUtils.isEmpty(userDto.getPhone())|| StringUtils.isEmpty(userDto.getCode())|| StringUtils.isEmpty(userDto.getType())){throw new RuntimeException("請輸入完整信息"); }//校驗驗證碼//從內存中獲取驗證碼String codeValue = (String) redisTemplate.opsForValue().get(PetHomeConstant.BINDER_VERIFICATION_CODE + ":" + userDto.getPhone());if (codeValue == null) {throw new RuntimeException("驗證碼已過時"); }//比對用戶輸入的驗證碼和redis內存中的驗證碼是否相等if (!codeValue.split(":")[0].toLowerCase().equals(userDto.getCode().toLowerCase())) {throw new RuntimeException("驗證碼錯誤"); } } }複製代碼
隨機生成訂單號
/** * 存放經緯度 */@Datapublic class Point {//經度private Double lng;//維度private Double lat; }package cn.itsource.pethome.basic.util;import java.math.BigDecimal;import java.text.SimpleDateFormat;import java.util.Calendar;import java.util.Date;public class CodeGenerateUtils { /** * 獲取商品編碼 * 商品編碼規則:nanoTime(後5位)*5位隨機數(10000~99999) * @return */public static String generateProductCode(){ long nanoPart = System.nanoTime() % 100000L; if(nanoPart<10000L){ nanoPart+=10000L; } long randomPart = (long)(Math.random()*(90000)+10000); String code = "0"+String.valueOf((new BigDecimal(nanoPart).multiply(new BigDecimal(randomPart)))); return code.substring(code.length()-10); } /** * @param id: 用戶id * 生成訂單編號 * 訂單編號規則:(10位):(年底尾*月,取後2位)+(用戶ID%3.33*日取整後2位)+(timestamp*10000之內隨機數,取後6位) * @return */public static String generateOrderSn(long id){ Calendar calendar = Calendar.getInstance(); int year = calendar.get(Calendar.YEAR); year = year % 10; if(year == 0) year = 10; int month = calendar.get(Calendar.MONTH)+1; int yearMonth = year * month; String yearMonthPart = "0"+yearMonth; yearMonthPart = yearMonthPart.substring(yearMonthPart.length() - 2 ); int day = calendar.get(Calendar.DAY_OF_MONTH); int dayNum = (int)((id % 3.33) * day); String dayPart = "0"+dayNum; dayPart = dayPart.substring(dayPart.length() - 2); String timestampPart = ""+(Math.random() * 10000) * (System.currentTimeMillis()/10000); timestampPart = timestampPart.replace(".", "").replace("E", ""); timestampPart = timestampPart.substring(0,6); return yearMonthPart+dayPart+timestampPart; } /** * 生成統一支付單號 * 規則:年(2)月(2)日(2)時(2)分(2)+timestamp*5位隨機整數取後5位 * @return */public static String generateUnionPaySn(){ Calendar calendar = Calendar.getInstance(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmm"); String dateTime = dateFormat.format(calendar.getTime()); dateTime = dateTime.substring(2); String timestampPart = ""+(Math.random() * 10000) * (System.currentTimeMillis()/10000); timestampPart = timestampPart.replace(".", "").replace("E", ""); timestampPart = timestampPart.substring(0,5); return dateTime+timestampPart; } public static void main(String[] args) { for(long i=0;i<100;i++) { //String timestampPart = ""+(Math.random() * 10000) * (System.currentTimeMillis()/10000);//System.out.println(timestampPart);//System.out.println(generateOrderSn(i)); System.out.println(generateUnionPaySn()); } } }複製代碼
經過地址獲取經緯度距離
package cn.itsource.pethome.basic.util;import cn.itsource.pethome.org.domain.Shop;import java.io.InputStream;import java.io.InputStreamReader;import java.net.HttpURLConnection;import java.net.URL;import java.util.List;/** * 位置相關工具類 */public class DistanceUtil {/** * 經過地址轉爲經緯度 * @param address * @return */public static Point getPoint(String address){ String Application_ID="PQ9FAt6qg7taDWj6LLABYO7u6bSETXhD";//配置上本身的百度地圖應用的AKtry{ String sCurrentLine; String sTotalString;sCurrentLine =""; sTotalString = ""; InputStream l_urlStream; URL l_url = new URL("http://api.map.baidu.com/geocoding/v3/?address="+address+"&output=json&ak="+Application_ID+"&callback=showLocation"); HttpURLConnection l_connection = (HttpURLConnection) l_url.openConnection(); l_connection.connect(); l_urlStream = l_connection.getInputStream(); java.io.BufferedReader l_reader = new java.io.BufferedReader(new InputStreamReader(l_urlStream)); String str=l_reader.readLine(); System.out.println(str);//用經度分割返回的網頁代碼 String s=","+"\""+"lat"+"\""+":"; String strs[]=str.split(s,2); String s1="\""+"lng"+"\""+":"; String a[]=strs[0].split(s1, 2); s1="}"+","+"\""; String a1[]=strs[1].split(s1,2); Point point=new Point(); point.setLng(Double.valueOf(a[1])); point.setLat(Double.valueOf(a1[0]));return point; } catch (Exception e) { e.printStackTrace();return null; } }//地球半徑,進行經緯度運算須要用到的數據之一private static final double EARTH_RADIUS = 6378137;//根據座標點獲取弧度private static double rad(double d){return d * Math.PI / 180.0; }/** * 根據兩點間經緯度座標(double值),計算兩點間距離,單位爲米 * @param point1 A點座標 * @param point2 B點座標 * @return */public static double getDistance(Point point1,Point point2){double radLat1 = rad(point1.getLat());double radLat2 = rad(point2.getLat());double a = radLat1 - radLat2;double b = rad(point1.getLng()) - rad(point2.getLng());double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) + Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2))); s = s * EARTH_RADIUS; s = Math.round(s * 10000) / 10000;return s; }/** * 根據兩點間經緯度座標(double值),計算兩點間距離,單位爲米 * @param point 用戶指定的地址座標 * @param shops 商店 * @return */public static Shop getNearestShop (Point point, List<Shop> shops) {//若是傳過來的集合只有一家店鋪,那麼直接將這家店鋪的信息返回就是最近的店鋪了Shop shop=shops.get(0);//獲取集合中第一家店鋪到指定地點的距離double distance=getDistance(point,getPoint(shops.get(0).getAddress()));//若是有多家店鋪,那麼就和第一家店鋪到指定地點的距離作比較if (shops.size()>1){for (int i=1;i<shops.size();i++){if (getDistance(point,getPoint(shops.get(i).getAddress()))<distance){ shop=shops.get(i); } } }return shop; }public static void main(String[] args) { System.out.println(getPoint("成都市武侯區天府新谷-10號樓")); } }複製代碼
fastdfs上傳下載文件
package cn.itsource.pethome.basic.util;import cn.itsource.pethome.org.domain.Shop;import java.io.InputStream;import java.io.InputStreamReader;import java.net.HttpURLConnection;import java.net.URL;import java.util.List;/** * 位置相關工具類 */public class DistanceUtil {/** * 經過地址轉爲經緯度 * @param address * @return */public static Point getPoint(String address){ String Application_ID="PQ9FAt6qg7taDWj6LLABYO7u6bSETXhD";//配置上本身的百度地圖應用的AKtry{ String sCurrentLine; String sTotalString;sCurrentLine =""; sTotalString = ""; InputStream l_urlStream; URL l_url = new URL("http://api.map.baidu.com/geocoding/v3/?address="+address+"&output=json&ak="+Application_ID+"&callback=showLocation"); HttpURLConnection l_connection = (HttpURLConnection) l_url.openConnection(); l_connection.connect(); l_urlStream = l_connection.getInputStream(); java.io.BufferedReader l_reader = new java.io.BufferedReader(new InputStreamReader(l_urlStream)); String str=l_reader.readLine(); System.out.println(str);//用經度分割返回的網頁代碼 String s=","+"\""+"lat"+"\""+":"; String strs[]=str.split(s,2); String s1="\""+"lng"+"\""+":"; String a[]=strs[0].split(s1, 2); s1="}"+","+"\""; String a1[]=strs[1].split(s1,2); Point point=new Point(); point.setLng(Double.valueOf(a[1])); point.setLat(Double.valueOf(a1[0]));return point; } catch (Exception e) { e.printStackTrace();return null; } }//地球半徑,進行經緯度運算須要用到的數據之一private static final double EARTH_RADIUS = 6378137;//根據座標點獲取弧度private static double rad(double d){return d * Math.PI / 180.0; }/** * 根據兩點間經緯度座標(double值),計算兩點間距離,單位爲米 * @param point1 A點座標 * @param point2 B點座標 * @return */public static double getDistance(Point point1,Point point2){double radLat1 = rad(point1.getLat());double radLat2 = rad(point2.getLat());double a = radLat1 - radLat2;double b = rad(point1.getLng()) - rad(point2.getLng());double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) + Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2))); s = s * EARTH_RADIUS; s = Math.round(s * 10000) / 10000;return s; }/** * 根據兩點間經緯度座標(double值),計算兩點間距離,單位爲米 * @param point 用戶指定的地址座標 * @param shops 商店 * @return */public static Shop getNearestShop (Point point, List<Shop> shops) {//若是傳過來的集合只有一家店鋪,那麼直接將這家店鋪的信息返回就是最近的店鋪了Shop shop=shops.get(0);//獲取集合中第一家店鋪到指定地點的距離double distance=getDistance(point,getPoint(shops.get(0).getAddress()));//若是有多家店鋪,那麼就和第一家店鋪到指定地點的距離作比較if (shops.size()>1){for (int i=1;i<shops.size();i++){if (getDistance(point,getPoint(shops.get(i).getAddress()))<distance){ shop=shops.get(i); } } }return shop; }public static void main(String[] args) { System.out.println(getPoint("成都市武侯區天府新谷-10號樓")); } }複製代碼
經常使用包
<!--對redis的支持--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- 模擬發送http請求 --><dependency><groupId>commons-httpclient</groupId><artifactId>commons-httpclient</artifactId><version>3.1</version></dependency><!--處理json--><!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.58</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.7</version></dependency><!--支付寶支付所需jar包--><dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>4.9.13.ALL</version></dependency>複製代碼