Browse Source

:ambulance: JWT验证优化,添加商品收藏功能

nnkwrik 6 years ago
parent
commit
e5779e53a9

+ 4 - 4
auth-service/pom.xml

@@ -26,10 +26,10 @@
     </properties>
 
     <dependencies>
-        <!--<dependency>-->
-            <!--<groupId>org.springframework.cloud</groupId>-->
-            <!--<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
-        <!--</dependency>-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>org.springframework.boot</groupId>

+ 3 - 4
auth-service/src/main/java/io/github/nnkwrik/authservice/AuthServiceApplication.java

@@ -2,12 +2,11 @@ package io.github.nnkwrik.authservice;
 
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.context.annotation.ComponentScan;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 
 
-@SpringBootApplication
-//@EnableDiscoveryClient
-@ComponentScan(basePackages = "io.github.nnkwrik")
+@SpringBootApplication(scanBasePackages = "io.github.nnkwrik")
+@EnableDiscoveryClient
 public class AuthServiceApplication {
 
     public static void main(String[] args) {

+ 0 - 1
auth-service/src/main/java/io/github/nnkwrik/authservice/dto/AuthDTO.java

@@ -10,5 +10,4 @@ import lombok.Data;
 public class AuthDTO {
     private String code;
     private DetailAuthDTO detail;
-    private String expiredToken;
 }

+ 1 - 0
common/src/main/java/io/github/nnkwrik/common/dto/Response.java

@@ -14,6 +14,7 @@ public class Response<T> {
     public static final int CHECK_USER_WITH_SESSION_FAIL = 3002;
     public static final int TOKEN_IS_EMPTY = 3003;
     public static final int TOKEN_IS_EXPIRED = 3004;
+    public static final int TOKEN_IS_WRONG = 3005;
 
     //goods
     public static final int OPEN_ID_IS_EMPTY = 4001;

+ 1 - 0
common/src/main/java/io/github/nnkwrik/common/exception/JWTException.java

@@ -11,6 +11,7 @@ import lombok.Data;
 public class JWTException extends RuntimeException {
     public static final int TOKEN_IS_EMPTY = Response.TOKEN_IS_EMPTY;
     public static final int TOKEN_IS_EXPIRED = Response.TOKEN_IS_EXPIRED;
+    public static final int TOKEN_IS_WRONG = Response.TOKEN_IS_WRONG;
 
     private int errno;
     private String errmsg;

+ 2 - 0
common/src/main/java/io/github/nnkwrik/common/token/injection/JWT.java

@@ -14,4 +14,6 @@ import java.lang.annotation.Target;
 public @interface JWT {
 
     boolean required() default false;
+
+    boolean checkExpired() default true;
 }

+ 8 - 7
common/src/main/java/io/github/nnkwrik/common/token/injection/JWTResolver.java

@@ -44,9 +44,9 @@ public class JWTResolver implements HandlerMethodArgumentResolver {
                                   WebDataBinderFactory binderFactory) {
         String token = webRequest.getHeader("Authorization");
         JWTUser user = null;
-        boolean isExpired = false;
-        if (token == null) {
+        if (token == null && parameter.getParameterAnnotation(JWT.class).required()) {
             log.info("用户的Authorization头为空,无法获取jwt");
+            throw new JWTException(JWTException.TOKEN_IS_EMPTY, "用户的Authorization头为空,无法获取jwt");
         } else if ((user = cache.getIfPresent(token)) == null) {   //试图从缓存获取
 
             try {
@@ -54,16 +54,17 @@ public class JWTResolver implements HandlerMethodArgumentResolver {
                 cache.put(token, user);
             } catch (TokenExpiredException e) {
                 log.info("jwt已过期,过期时间:{}", e.getMessage());
-                isExpired = true;
+                if (parameter.getParameterAnnotation(JWT.class).checkExpired()){
+                    throw new JWTException(JWTException.TOKEN_IS_EXPIRED, "凭证已过期");
+                }
             } catch (Exception e) {
                 log.info("jwt解析失败");
+                if (parameter.getParameterAnnotation(JWT.class).required()){
+                    throw new JWTException(JWTException.TOKEN_IS_WRONG, "用户的Authorization头错误,无法获取jwt");
+                }
             }
         }
 
-        if (user == null && parameter.getParameterAnnotation(JWT.class).required()) {
-            if (isExpired) throw new JWTException(JWTException.TOKEN_IS_EXPIRED, "凭证已过期");
-            throw new JWTException(JWTException.TOKEN_IS_EMPTY, "用户的Authorization头错误,无法获取jwt");
-        }
         log.info("jwt解析结果为:{}", user);
 
         return user;

+ 13 - 0
goods-service/src/main/java/io/github/nnkwrik/goodsservice/controller/IndexController.java

@@ -1,6 +1,8 @@
 package io.github.nnkwrik.goodsservice.controller;
 
+import io.github.nnkwrik.common.dto.JWTUser;
 import io.github.nnkwrik.common.dto.Response;
+import io.github.nnkwrik.common.token.injection.JWT;
 import io.github.nnkwrik.goodsservice.model.vo.CatalogVo;
 import io.github.nnkwrik.goodsservice.model.vo.IndexVO;
 import io.github.nnkwrik.goodsservice.service.IndexService;
@@ -8,6 +10,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 /**
@@ -47,4 +50,14 @@ public class IndexController {
 
         return Response.ok(vo);
     }
+
+    @PostMapping("/collect/addordelete/{goodsId}/{userHasCollect}")
+    public Response collectAddOrDelete(@PathVariable("goodsId") int goodsId,
+                                       @PathVariable("userHasCollect") boolean hasCollect,
+                                       @JWT(required = true) JWTUser user) {
+        indexService.collectAddOrDelete(goodsId, user.getOpenId(), hasCollect);
+        log.info("用户【{}】添加或删除收藏商品,商品id={},是否是添加?{}", user.getNickName(), goodsId, !hasCollect);
+        return Response.ok();
+
+    }
 }

+ 25 - 3
goods-service/src/main/java/io/github/nnkwrik/goodsservice/dao/OtherMapper.java

@@ -2,9 +2,7 @@ package io.github.nnkwrik.goodsservice.dao;
 
 import io.github.nnkwrik.goodsservice.model.po.Ad;
 import io.github.nnkwrik.goodsservice.model.po.Channel;
-import org.apache.ibatis.annotations.Mapper;
-import org.apache.ibatis.annotations.Param;
-import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.*;
 
 import java.util.List;
 
@@ -29,7 +27,31 @@ public interface OtherMapper {
     Boolean userHasCollect(@Param("user_id") String userId, @Param("goods_id") int goodsId);
 
 
+    @Insert("insert into user_preference (goods_id, user_id, type)\n" +
+            "values (#{goods_id}, #{user_id}, 1);\n")
+    void setUserCollect(@Param("user_id") String userId, @Param("goods_id") int goodsId);
+
+    @Delete("delete\n" +
+            "from user_preference\n" +
+            "where goods_id = #{goods_id}\n" +
+            "  and user_id = #{user_id}\n" +
+            "  and type = 1;")
+    void deleteUserCollect(@Param("user_id") String userId, @Param("goods_id") int goodsId);
+
+
+
     @Select("SELECT EXISTS(SELECT 1 FROM user_preference WHERE type = 2 and user_id = #{user_id} and goods_id = #{goods_id})")
     Boolean userHasWant(@Param("user_id") String userId, @Param("goods_id") int goodsId);
 
+    @Insert("insert into user_preference (goods_id, user_id, type)\n" +
+            "values (#{goods_id}, #{user_id}, 2);\n")
+    void setUserWant(@Param("user_id") String userId, @Param("goods_id") int goodsId);
+
+    @Delete("delete\n" +
+            "from user_preference\n" +
+            "where goods_id = #{goods_id}\n" +
+            "  and user_id = #{user_id}\n" +
+            "  and type = 2;")
+    void deleteUserWant(@Param("user_id") String userId, @Param("goods_id") int goodsId);
+
 }

+ 2 - 0
goods-service/src/main/java/io/github/nnkwrik/goodsservice/service/IndexService.java

@@ -15,5 +15,7 @@ public interface IndexService {
 
     CatalogVo getCatalogById(int id);
 
+    void collectAddOrDelete(int goodsId, String userId, boolean hasCollect);
+
 
 }

+ 9 - 0
goods-service/src/main/java/io/github/nnkwrik/goodsservice/service/impl/IndexServiceImpl.java

@@ -80,5 +80,14 @@ public class IndexServiceImpl implements IndexService {
         return new CatalogVo(null, currentCategoryVo);
     }
 
+    @Override
+    public void collectAddOrDelete(int goodsId, String userId, boolean hasCollect) {
+        if (hasCollect) {
+            otherMapper.deleteUserCollect(userId, goodsId);
+        } else {
+            otherMapper.setUserCollect(userId, goodsId);
+        }
+    }
+
 
 }

+ 6 - 6
wx-front/app.js

@@ -5,20 +5,20 @@ var user = require('./services/user.js');
 App({
   onLaunch: function () {
     //!!生产环境专用测试数据
-    wx.setStorageSync('userInfo', this.testData.userInfo);
-    wx.setStorageSync('token', this.testData.token);
+    // wx.setStorageSync('userInfo', this.testData.userInfo);
+    // wx.setStorageSync('token', this.testData.token);
 
     // wx.setStorageSync('userInfo', null);
     // wx.setStorageSync('token', null);
 
     //获取用户的登录信息
-    // user.checkLogin().then(res => {
-    //   console.log('app login')
+    user.checkLogin().then(res => {
+      console.log('app login')
       this.globalData.userInfo = wx.getStorageSync('userInfo');
       this.globalData.token = wx.getStorageSync('token');
-    // }).catch(() => {
+    }).catch(() => {
       
-    // });
+    });
   },
   
   globalData: {

+ 1 - 1
wx-front/config/api.js

@@ -1,5 +1,5 @@
 //const ApiRootUrl = 'https://23788fbf.ngrok.io/';
-const ApiRootUrl = 'http://127.0.0.1:8804/';
+const ApiRootUrl = 'http://127.0.0.1:8080/';
 
 module.exports = {
   IndexUrl: ApiRootUrl + 'index/index', //首页数据接口

+ 2 - 34
wx-front/pages/auth/auth.js

@@ -22,46 +22,14 @@ Page({
 
   startLogin: function(e) {
     console.log(e);
-    this.login(e.detail).then((userInfo) => {
+    util.backendLogin(e.detail).then((userInfo) => {
       this.setData({
         userInfo: userInfo,
         hasUserInfo: true
       })
     });
   },
-
-  login: function (detail) {
-    let code = null;
-    return new Promise(function (resolve, reject) {
-      return util.login().then((res) => {
-        code = res.code;
-      }).then(() => {
-        //登录远程服务器
-        util.request(api.AuthLoginByWeixin, {
-          code: code,
-          detail: detail
-        }, 'POST').then(res => {
-          if (res.errno === 0) {
-            //存储用户信息
-            wx.setStorageSync('userInfo', res.data.userInfo);
-            wx.setStorageSync('token', res.data.token);
-
-            //反应到当前登录
-            app.globalData.userInfo = res.data.userInfo;
-            app.globalData.token = res.data.token;
-
-            resolve(res.data.userInfo);
-          } else {
-            reject(res.data.userInfo);
-          }
-        }).catch((err) => { //request
-          reject(err);
-        });
-      }).catch((err) => {   //login
-        reject(err);
-      })
-    });
-  },
+  
   goback: function () {
     wx.navigateBack({
       delta: 1

+ 2 - 2
wx-front/pages/auth/auth.wxml

@@ -3,8 +3,8 @@
 
     <view class="userinfo">
       <block>
-        <image class="userinfo-avatar" src="{{userInfo.avatar}}" mode="cover"></image>
-        <text class="userinfo-nickname">{{userInfo.nickname}}</text>
+        <image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" mode="cover"></image>
+        <text class="userinfo-nickname">{{userInfo.nickName}}</text>
       </block>
     </view>
 

+ 7 - 2
wx-front/pages/goods/goods.js

@@ -225,11 +225,16 @@ Page({
   addCannelCollect: function () {
     let that = this;
     //添加或是取消收藏
-    util.request(api.CollectAddOrDelete, { typeId: 0, valueId: this.data.id }, "POST")
+    util.request(api.CollectAddOrDelete + '/' + this.data.id + '/' + this.data.userHasCollect,{}, "POST")
       .then(function (res) {
         let _res = res;
+        let collectState = !that.data.userHasCollect;
         if (_res.errno == 0) {
-          if (_res.data.type == 'add') {
+          that.setData({
+            userHasCollect: collectState
+          });
+          
+          if (that.data.userHasCollect) {
             that.setData({
               'collectBackImage': that.data.hasCollectImage
             });

+ 1 - 1
wx-front/pages/goods/goods.wxml

@@ -113,7 +113,7 @@
               <view class="time">{{iitem.create_time}}</view>
             </view>
             <view class="reply-content">
-              回复@{{iitem.reply_user_name}}:{{item.content}}
+              回复@{{iitem.reply_user_name}}:{{iitem.content}}
             </view>
           </view>
 

+ 2 - 0
wx-front/pages/goods/goods.wxss

@@ -235,6 +235,7 @@
   height: 67rpx;
   line-height: 67rpx;
   font-size: 0;
+  overflow:hidden;
 }
 
 .comments .reply-user {
@@ -244,6 +245,7 @@
   line-height: 67rpx;
   font-size: 0;
   margin-left: 50rpx;
+  overflow:hidden;
 }
 
 .comments .reply-user  image {

+ 1 - 1
wx-front/pages/index/index.wxss

@@ -60,7 +60,7 @@
 
 .a-section .h .txt {
   padding-right: 30rpx;
-  background: url("http://ac-3yr0g9cz.clouddn.com/2cdba05369e10f934e54.png") right 4rpx no-repeat;
+  /* background: url("http://ac-3yr0g9cz.clouddn.com/2cdba05369e10f934e54.png") right 4rpx no-repeat; */
   background-size: 16.656rpx 27rpx;
   display: inline-block;
   height: 36rpx;

+ 6 - 5
wx-front/services/user.js

@@ -48,12 +48,13 @@ function loginByWeixin() {
 function checkLogin() {
   return new Promise(function(resolve, reject) {
     if (wx.getStorageSync('userInfo') && wx.getStorageSync('token')) {
+      resolve(true);
 
-      util.checkSession().then(() => {
-        resolve(true);
-      }).catch(() => {
-        reject(false); //session过期
-      });
+      // util.checkSession().then(() => {
+      //   resolve(true);
+      // }).catch(() => {
+      //   reject(false); //session过期
+      // });
 
     } else {
       reject(false); //没有登陆过

+ 61 - 39
wx-front/utils/util.js

@@ -22,6 +22,9 @@ function formatNumber(n) {
  * 封封微信的的request
  */
 function request(url, data = {}, method = "GET") {
+  let that = this
+  let token = wx.getStorageSync('token')
+  
   return new Promise(function (resolve, reject) {
     wx.request({
       url: url,
@@ -29,53 +32,34 @@ function request(url, data = {}, method = "GET") {
       method: method,
       header: {
         'Content-Type': 'application/json',
-        'Authorization': wx.getStorageSync('token')
+        'Authorization': token
       },
       success: function (res) {
         console.log("success");
 
         if (res.statusCode == 200) {
 
-          if (res.data.errno == 3003) {
+          if (res.data.errno == 3003 || res.data.errno == 3004 || res.data.errno == 3005) {
+            console.log(res.data.errmsg)
             //TOKEN_IS_EMPTY
             //需要登录后才可以操作
-            wx.navigateTo({
-              url: '/pages/auth/auth'
-            })
-          } else if (res.data.errno == 3004){
-            // TOKEN_IS_EXPIRED
-            
-            let code = null;
-            return new Promise(function (resolve, reject) {
-              return util.login().then((res) => {
-                code = res.code;
-              }).then(() => {
-                //登录远程服务器
-                util.request(api.AuthLoginByWeixin, {
-                  code: code,
-                  detail: detail,
-                  expiredToken: wx.getStorageSync('token')
-                }, 'POST').then(res => {
-                  if (res.errno === 0) {
-                    //存储用户信息
-                    wx.setStorageSync('userInfo', res.data.userInfo);
-                    wx.setStorageSync('token', res.data.token);
-
-                    //反应到当前登录
-                    app.globalData.userInfo = res.data.userInfo;
-                    app.globalData.token = res.data.token;
-
-                    resolve(res.data.userInfo);
-                  } else {
-                    reject(res.data.userInfo);
-                  }
-                }).catch((err) => { //request
-                  reject(err);
-                });
-              }).catch((err) => {   //login
-                reject(err);
-              })
-            });
+            wx.getSetting({
+              success: res => {
+                if (res.authSetting['scope.userInfo']) {
+                  // // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
+                  that.getUserInfo().then((res) => {
+                    that.backendLogin(res).then(()=>{
+                      that.request(url,data,method)
+                    })
+                  })
+
+                }else{
+                  wx.navigateTo({
+                    url: '/pages/auth/auth'
+                  })
+                }
+              }
+            })   
 
           } else {
             resolve(res.data);
@@ -93,6 +77,8 @@ function request(url, data = {}, method = "GET") {
   });
 }
 
+
+
 /**
  * 检查微信会话是否过期
  */
@@ -109,6 +95,40 @@ function checkSession() {
   });
 }
 
+/**
+ * 在后端服务器进行登录
+ */
+function backendLogin(detail) {
+  console.log("在后端服务器进行登录" + detail)
+  let that = this;
+  let code = null;
+  return new Promise(function (resolve, reject) {
+    return that.login().then((res) => {
+      code = res.code;
+    }).then(() => {
+      //登录远程服务器
+      that.request(api.AuthLoginByWeixin, {
+        code: code,
+        detail: detail
+      }, 'POST').then(res => {
+        if (res.errno === 0) {
+          //存储用户信息
+          wx.setStorageSync('userInfo', res.data.userInfo);
+          wx.setStorageSync('token', res.data.token);
+
+            resolve(res.data.userInfo);
+          } else {
+            reject(res.data.userInfo);
+          }
+      }).catch((err) => { //request
+        reject(err);
+      });
+    }).catch((err) => {   //login
+      reject(err);
+    })
+  });
+}
+
 /**
  * 调用微信登录
  */
@@ -131,6 +151,7 @@ function login() {
   });
 }
 
+
 function getUserInfo() {
   return new Promise(function (resolve, reject) {
     wx.getUserInfo({
@@ -176,6 +197,7 @@ module.exports = {
   checkSession,
   login,
   getUserInfo,
+  backendLogin,
 }
 
 

+ 13 - 1
zuul/src/main/resources/application.yml

@@ -7,7 +7,6 @@ eureka:
 spring:
   application:
     name: api-gateway
-
 zuul:
   routes:
     #auth-service
@@ -15,23 +14,36 @@ zuul:
       path: /auth/**
       serviceId: auth-service
       strip-prefix: false
+      #过滤hearder的黑名单,默认为 sensitiveHeaders:Cookie,Set-Cookie,Authorization
+      #手动把它设置为空,否则包含JWT信息的Authorization头无法传递到下游服务
+      sensitiveHeaders:
     #goods-service
     goods:
       path: /goods/**
       serviceId: goods-service
       strip-prefix: false
+      sensitiveHeaders:
     index:
       path: /index/**
       serviceId: goods-service
       strip-prefix: false
+      sensitiveHeaders:
     catalog:
       path: /catalog/**
       serviceId: goods-service
       strip-prefix: false
+      sensitiveHeaders:
     search:
       path: /search/**
       serviceId: goods-service
       strip-prefix: false
+      sensitiveHeaders:
+    collect:
+      path: /collect/**
+      serviceId: goods-service
+      strip-prefix: false
+      sensitiveHeaders:
+