簡單的註冊登錄(第三方微信登錄)

註冊登錄

註冊包括郵箱註冊和手機號註冊,兩種註冊方法業務都相同,只是激活方式不一樣javascript

註冊模塊:

  1. 用戶在頁面輸入郵箱/手機號,點擊獲取驗證碼,就會向後臺發送post請求php

  2. 後端接收到前端請求,發送驗證碼html

  • 經過'工具類',生成一個隨機四位數做爲驗證碼
  • 將驗證碼保存到**'redis'**中,redis保存數據是經過鍵值對的方式保存
    • 因此咱們能夠經過電話+一個固定的常量做爲key值
    • 隨機數+當前毫秒值做爲value值
    • 注意設置有效期,通常爲5分鐘
  • 注意當用戶點擊獲取驗證碼時,咱們要判斷用戶是否已經點擊過獲取驗證碼
    • 經過輸入的手機號,按照咱們拼接key值的方式拼接,而後從redis中獲取
    • 若是獲取到的value值不爲空,表示這不是第一次點擊獲取,此時咱們須要判斷當前時間和第一次點擊獲取的時間,若是小於一分鐘,拋出異常,告訴用戶不要重複獲取驗證碼,若是超過了一分鐘,此時咱們的驗證碼仍是等於第一次獲取的驗證碼(這樣作是爲了防止網絡問題致使驗證碼累計問題)
    • 若是value值爲空時,代表是第一次獲取驗證碼,直接拼接驗證碼發送給用戶
  • 而後調用**'發送驗證碼工具類'**發送驗證碼給用戶
  1. 用戶在頁面輸入收到的驗證碼,點擊註冊
  • 向後臺發送請求,帶有註冊的信息前端

  • 後端響應請求獲取請求攜帶的參數,此時咱們可使用一個臨時對象接受參數(方便定義添加屬性)java

  • 首先第一件事就是進行校驗ios

    • 判斷驗證碼是否爲空,若是爲空表示已過時,拋出異常告訴用戶驗證碼過時,請從新獲取驗證碼
    • 若是不爲空,比較用戶輸入的驗證碼和保存在redis緩存中的驗證碼是否相等,若是不等,拋出異常告訴用戶驗證碼錯誤
    • 校驗數據是否爲空,若是爲空,拋出異常提示用戶輸入完整的信息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查詢數據庫中是否有對應的登陸信息

    • 若是沒有,則沒有登陸,返回一個未登陸的狀態給前端,若是有就放行(注意配置攔截器攔截和放行的頁面,好比註冊和登錄的頁面必須放行)
    • 若是有,返回true,同時再設置一次token有效期
  • axios前端後置攔截器:判斷後端攔截器返回的狀態,若是爲未登陸狀態,則跳轉到登陸頁面,若是已登陸狀態,則跳轉到請求訪問的頁面 **問題?**返回什麼結果如何跳轉到請求頁面

微信第三方登錄

  • 用戶點擊微信登錄時後端響應請求,拉取二維碼,重定向到二維碼展現界面
  • 用戶掃碼確認登錄,執行回調函數並攜帶了受權碼,咱們設計了一個前端頁面來響應這個回調函數,向後端發送請求獲取登錄信息對象
  • 後端響應請求,經過受權碼獲取用戶的token信息地址url,咱們**經過httpClient插件向token信息地址獲取token和微信用戶惟一標識**
  • 經過token和惟一標識就能獲取登陸的微信用戶的信息
    • 咱們經過微信中的惟一標識去微信用戶表查詢,若是存在就微信用戶,繼續判斷是否有登錄信息,若是登錄信息不爲空,直接登錄
    • 若是沒有登錄信息,就返回微信的用戶的惟一標識,此時前端判斷沒有登錄信息只有惟一標識,跳轉到綁定頁面
    • 若是有登錄信息,咱們就建立token,放入redis中,並建立map放入token和登錄信息對象返回前端,前端判斷有登錄信息,放入localstorage中,而後跳轉到後臺首頁

相關工具代碼

隨機數工具類

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();
    }

}複製代碼

redis

瞭解數據庫

數據庫分類

  • NoSQL非關係型數據庫:存儲數據都是沒有結構的(沒有表的概念),而且存儲數據都是以key-value的的方式存儲,都是把數據存儲到內存或者磁盤上

  • RDBSM關係型數據庫:是有行和列組成的二維表,存儲數據是有必定格式的,都是把數據存儲到磁盤上

    區別:

  1. 關係型數據庫不支持高併發訪問,非關係型數據庫支持高併發訪問
  2. 關係型數據庫存儲數據都是有結構的,而非關係型數據庫存儲數據是沒有結構的(,沒有表概念)
  3. 關係型數據庫存儲數據只能放到磁盤上,非關係型數據庫存儲數據是放在內存和磁盤上
  4. 關係型數據庫的結構和數據存儲都是有限的(列最多200列,存儲數據量也是有限的,最多不超過200萬),而非關係型數據庫只要硬件夠好,數據量是沒有限制的

Redis是什麼

Redis是一個高性能的開源的,c語言寫的NoSQL,數據保存在內存/磁盤中。

Redis是以key-value形式存儲,不必定遵循傳統數據庫的一些基本要求,好比不遵循sql標準,事務,表結構等,redis嚴格說來不是一個數據庫,應該是一種數據結構化存儲方法的集合

存儲數據都是以字符串的形式進行存儲,把存儲的字符串按必定的規則進行擺放(規則:String list set zset Hash)

特色

  1. 開源免費
  2. 支持高併發,讀取速度很是快
  3. 存儲數據放到內存或者磁盤
  4. 支持多種類型的客戶端訪問(c,php,java)
  5. 支持集羣(集羣的意思:一臺不夠用就再加一臺)

使用場景

  1. 作中央緩存
  2. 點贊計數器
  3. 防***系統

使用redis保存驗證碼

綠色版的在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>複製代碼
相關文章
相關標籤/搜索