五、基于jwt token的无状态登陆

一、登陆的流程:

携带用户名密码的登陆请求经过网关转发到授权中心微服务,授权微服务远程调用user-service中的登陆方法验证用户名和密码,验证成功之后,将userId,userName作为载荷加入到token中,在将token写入到cookie中

二、用户微服务中验证用户名和密码的方法
@GetMapping("query")
 public ResponseEntity<User> queryUser(@RequestParam("username")String username,@RequestParam("password")String password){
     
     User user = this.userService.queryUser(username, password);
     if (user == null){
     
         return ResponseEntity.badRequest().build();
     }
     return ResponseEntity.ok(user);
 }
public User queryUser(String username, String password) {
     
    // 根据用户名去数据库中查找用户
    User record = new User();
    record.setUsername(username);
    User user = this.userMapper.selectOne(record);
    if (user == null){
     
        return null;
    }
    // 获取盐,对用户输入的密码进行加盐加密
    password = CodecUtils.md5Hex(password, user.getSalt());
    // 和数据库中的密码进行比较
    if (StringUtils.equals(password, user.getPassword())){
     
        return user;
    }
    return null;
}

由于是远程调用的方式,而不是直接请求到controller接口,我们将controller中的方法写成一个接口

public interface UserApi {
     
    @GetMapping("query")
    User queryUser(@RequestParam("username")String username, @RequestParam("password")String password);
}

五、基于jwt token的无状态登陆_第1张图片

三、授权微服务

项目结构
五、基于jwt token的无状态登陆_第2张图片
auth-common依赖:


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>leyou-authartifactId>
        <groupId>com.leyou.authgroupId>
        <version>1.0.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <groupId>com.leyou.authgroupId>
    <artifactId>leyou-auth-commonartifactId>

    <dependencies>
        <dependency>
            <groupId>org.apache.commonsgroupId>
            <artifactId>commons-lang3artifactId>
        dependency>

        <dependency>
            <groupId>io.jsonwebtokengroupId>
            <artifactId>jjwtartifactId>
            <version>0.9.0version>
        dependency>

        <dependency>
            <groupId>joda-timegroupId>
            <artifactId>joda-timeartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
        dependency>

    dependencies>
project>

auth-service依赖:


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>leyou-authartifactId>
        <groupId>com.leyou.authgroupId>
        <version>1.0.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <groupId>com.leyou.authgroupId>
    <artifactId>leyou-auth-serviceartifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>com.leyou.usergroupId>
            <artifactId>leyou-user-interfaceartifactId>
            <version>1.0.0-SNAPSHOTversion>
        dependency>

        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-openfeignartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
        dependency>

        <dependency>
            <groupId>com.leyou.authgroupId>
            <artifactId>leyou-auth-commonartifactId>
            <version>1.0.0-SNAPSHOTversion>
        dependency>

        <dependency>
            <groupId>com.leyou.commongroupId>
            <artifactId>leyou-commonartifactId>
            <version>1.0.0-SNAPSHOTversion>
        dependency>
    dependencies>
project>

auth-service配置:

server:
  port: 8087
spring:
  application:
    name: auth-service
eureka:
  client:
    service-url:
      defaultZone: http://localhost:10086/eureka
    registry-fetch-interval-seconds: 10
  instance:
    lease-expiration-duration-in-seconds: 15
    lease-renewal-interval-in-seconds: 5
leyou:
  jwt:
    secret: leyou@Login(Auth}*^31)&heiMa% # secret生成公钥和私钥的密文
    pubKeyPath: D:\IdeaProjects\springCloud\tmp\rsa\rsa.pub # 公钥地址
    priKeyPath: D:\IdeaProjects\springCloud\tmp\rsa\rsa.pri # 私钥地址
    expire: 30 #token过期时间,单位分钟
    cookieName: LY_TOKEN #cookie的名字

用到的几个主要的工具类
RsaUtils:生成和获取私钥公钥的工具类

package com.leyou.common.utils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class RsaUtils {
     
    /**
     * 从文件中读取公钥
     *
     * @param filename 公钥保存路径,相对于classpath
     * @return 公钥对象
     * @throws Exception
     */
    public static PublicKey getPublicKey(String filename) throws Exception {
     
        byte[] bytes = readFile(filename);
        return getPublicKey(bytes);
    }

    /**
     * 从文件中读取私钥
     *
     * @param filename 私钥保存路径,相对于classpath
     * @return 私钥对象
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String filename) throws Exception {
     
        byte[] bytes = readFile(filename);
        return getPrivateKey(bytes);
    }

    /**
     * 获取公钥
     *
     * @param bytes 公钥的字节形式
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(byte[] bytes) throws Exception {
     
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePublic(spec);
    }

    /**
     * 获取私钥
     *
     * @param bytes 私钥的字节形式
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(byte[] bytes) throws Exception {
     
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePrivate(spec);
    }

    /**
     * 根据密文(secret),生存rsa公钥和私钥,并写入指定文件
     *
     * @param publicKeyFilename  公钥文件路径
     * @param privateKeyFilename 私钥文件路径
     * @param secret             生成密钥的密文
     * @throws IOException
     * @throws NoSuchAlgorithmException
     */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws Exception {
     
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        writeFile(publicKeyFilename, publicKeyBytes);
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        writeFile(privateKeyFilename, privateKeyBytes);
    }

    private static byte[] readFile(String fileName) throws Exception {
     
        return Files.readAllBytes(new File(fileName).toPath());
    }

    private static void writeFile(String destPath, byte[] bytes) throws IOException {
     
        File dest = new File(destPath);
        if (!dest.exists()) {
     
            dest.createNewFile();
        }
        Files.write(dest.toPath(), bytes);
    }
}

JwtUtils:生成和解析token的工具类

package com.leyou.common.utils;

import com.leyou.common.pojo.UserInfo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime;
import java.security.PrivateKey;
import java.security.PublicKey;

public class JwtUtils {
     
    /**
     * 私钥加密token
     *
     * @param userInfo      载荷中的数据
     * @param privateKey    私钥
     * @param expireMinutes 过期时间,单位分钟
     * @return
     * @throws Exception
     */
    public static String generateToken(UserInfo userInfo, PrivateKey privateKey, int expireMinutes) throws Exception {
     
        return Jwts.builder()
                .claim(JwtConstans.JWT_KEY_ID, userInfo.getId())
                .claim(JwtConstans.JWT_KEY_USER_NAME, userInfo.getUsername())
                .setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate())
                .signWith(SignatureAlgorithm.RS256, privateKey)
                .compact();
    }

    /**
     * 私钥加密token
     *
     * @param userInfo      载荷中的数据
     * @param privateKey    私钥字节数组
     * @param expireMinutes 过期时间,单位分钟
     * @return
     * @throws Exception
     */
    public static String generateToken(UserInfo userInfo, byte[] privateKey, int expireMinutes) throws Exception {
     
        return Jwts.builder()
                .claim(JwtConstans.JWT_KEY_ID, userInfo.getId())
                .claim(JwtConstans.JWT_KEY_USER_NAME, userInfo.getUsername())
                .setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate())
                .signWith(SignatureAlgorithm.RS256, RsaUtils.getPrivateKey(privateKey))
                .compact();
    }

    /**
     * 公钥解析token
     *
     * @param token     用户请求中的token
     * @param publicKey 公钥
     * @return
     * @throws Exception
     */
    private static Jws<Claims> parserToken(String token, PublicKey publicKey) {
     
        return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
    }

    /**
     * 公钥解析token
     *
     * @param token     用户请求中的token
     * @param publicKey 公钥字节数组
     * @return
     * @throws Exception
     */
    private static Jws<Claims> parserToken(String token, byte[] publicKey) throws Exception {
     
        return Jwts.parser().setSigningKey(RsaUtils.getPublicKey(publicKey))
                .parseClaimsJws(token);
    }

    /**
     * 获取token中的用户信息
     *
     * @param token     用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     * @throws Exception
     */
    public static UserInfo getInfoFromToken(String token, PublicKey publicKey) throws Exception {
     
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        return new UserInfo(
                ObjectUtils.toLong(body.get(JwtConstans.JWT_KEY_ID)),
                ObjectUtils.toString(body.get(JwtConstans.JWT_KEY_USER_NAME))
        );
    }

    /**
     * 获取token中的用户信息
     *
     * @param token     用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     * @throws Exception
     */
    public static UserInfo getInfoFromToken(String token, byte[] publicKey) throws Exception {
     
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        return new UserInfo(
                ObjectUtils.toLong(body.get(JwtConstans.JWT_KEY_ID)),
                ObjectUtils.toString(body.get(JwtConstans.JWT_KEY_USER_NAME))
        );
    }
}

CookieUtils:操作cookie的工具类

package com.leyou.common.utils;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

/**
 * 
 * Cookie 工具类
 *
 */
public final class CookieUtils {
     

	static final Logger logger = LoggerFactory.getLogger(CookieUtils.class);

	/**
	 * 得到Cookie的值, 不编码
	 * 
	 * @param request
	 * @param cookieName
	 * @return
	 */
	public static String getCookieValue(HttpServletRequest request, String cookieName) {
     
		return getCookieValue(request, cookieName, false);
	}

	/**
	 * 得到Cookie的值,
	 * 
	 * @param request
	 * @param cookieName
	 * @return
	 */
	public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
     
		Cookie[] cookieList = request.getCookies();
		if (cookieList == null || cookieName == null){
     
			return null;			
		}
		String retValue = null;
		try {
     
			for (int i = 0; i < cookieList.length; i++) {
     
				if (cookieList[i].getName().equals(cookieName)) {
     
					if (isDecoder) {
     
						retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
					} else {
     
						retValue = cookieList[i].getValue();
					}
					break;
				}
			}
		} catch (UnsupportedEncodingException e) {
     
			logger.error("Cookie Decode Error.", e);
		}
		return retValue;
	}

	/**
	 * 得到Cookie的值,
	 * 
	 * @param request
	 * @param cookieName
	 * @return
	 */
	public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
     
		Cookie[] cookieList = request.getCookies();
		if (cookieList == null || cookieName == null){
     
			return null;			
		}
		String retValue = null;
		try {
     
			for (int i = 0; i < cookieList.length; i++) {
     
				if (cookieList[i].getName().equals(cookieName)) {
     
					retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
					break;
				}
			}
		} catch (UnsupportedEncodingException e) {
     
			logger.error("Cookie Decode Error.", e);
		}
		return retValue;
	}

	/**
	 * 生成cookie,并指定编码
	 * @param request 请求
	 * @param response 响应
	 * @param cookieName name
	 * @param cookieValue value
	 * @param encodeString 编码
	 */
	public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, String encodeString) {
     
		setCookie(request,response,cookieName,cookieValue,null,encodeString, null);
	}

	/**
	 * 生成cookie,并指定生存时间
	 * @param request 请求
	 * @param response 响应
	 * @param cookieName name
	 * @param cookieValue value
	 * @param cookieMaxAge 生存时间
	 */
	public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge) {
     
		setCookie(request,response,cookieName,cookieValue,cookieMaxAge,null, null);
	}

	/**
	 * 设置cookie,不指定httpOnly属性
	 */
	public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge, String encodeString) {
     
		setCookie(request,response,cookieName,cookieValue,cookieMaxAge,encodeString, null);
	}

	/**
	 * 设置Cookie的值,并使其在指定时间内生效
	 * 
	 * @param cookieMaxAge
	 *        cookie生效的最大秒数
	 */
	public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge, String encodeString, Boolean httpOnly) {
     
		try {
     
			if(StringUtils.isBlank(encodeString)) {
     
				encodeString = "utf-8";
			}
			if (cookieValue == null) {
     
				cookieValue = "";
			} else {
     
				cookieValue = URLEncoder.encode(cookieValue, encodeString);
			}
			Cookie cookie = new Cookie(cookieName, cookieValue);
			if (cookieMaxAge != null && cookieMaxAge > 0)
				cookie.setMaxAge(cookieMaxAge);
			if (null != request)// 设置域名的cookie
				cookie.setDomain(getDomainName(request));
			cookie.setPath("/");

			if(httpOnly != null) {
     
				cookie.setHttpOnly(httpOnly);
			}
			response.addCookie(cookie);
		} catch (Exception e) {
     
			logger.error("Cookie Encode Error.", e);
		}
	}

	/**
	 * 得到cookie的域名
	 */
	private static final String getDomainName(HttpServletRequest request) {
     
		String domainName = null;
		String serverName = request.getRequestURL().toString();
		if (serverName == null || serverName.equals("")) {
     
			domainName = "";
		} else {
     
			serverName = serverName.toLowerCase();
			serverName = serverName.substring(7);
			final int end = serverName.indexOf("/");
			serverName = serverName.substring(0, end);
			final String[] domains = serverName.split("\\.");
			int len = domains.length;
			if (len > 3) {
     
				// www.xxx.com.cn
				domainName = domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
			} else if (len <= 3 && len > 1) {
     
				// xxx.com or xxx.cn
				domainName = domains[len - 2] + "." + domains[len - 1];
			} else {
     
				domainName = serverName;
			}
		}
		if (domainName != null && domainName.indexOf(":") > 0) {
     
			String[] ary = domainName.split("\\:");
			domainName = ary[0];
		}
		return domainName;
	}
}

JwtConstans:定义载荷常量

public abstract class JwtConstans {
     
    public static final String JWT_KEY_ID = "id";
    public static final String JWT_KEY_USER_NAME = "username";
}

JwtProperties:对应配置文件中的配置信息,和私钥公钥对象

//读取配置文件中前缀是leyou.jwt的配置信息赋值给下面的成员变量
@ConfigurationProperties(prefix = "leyou.jwt")
public class JwtProperties {
     
    private String secret;        // 密钥
    private String pubKeyPath;    // 公钥路径
    private String priKeyPath;    // 私钥路径
    private int expire;           // token过期时间
    private PublicKey publicKey;  // 公钥
    private PrivateKey privateKey;// 私钥
    private String cookieName;    // cookie名字
    private static final Logger logger = LoggerFactory.getLogger(JwtProperties.class);

    /**
     * @PostContruct:在构造方法执行之后执行该方法
     * 初始化publicKey,privateKey
     */
    @PostConstruct
    public void init(){
     
        try {
     
            File pubKey = new File(pubKeyPath);
            File priKey = new File(priKeyPath);
            if (!pubKey.exists() || !priKey.exists()) {
     
                // 生成公钥和私钥
                RsaUtils.generateKey(pubKeyPath, priKeyPath, secret);
            }
            // 获取公钥和私钥
            this.publicKey = RsaUtils.getPublicKey(pubKeyPath);
            this.privateKey = RsaUtils.getPrivateKey(priKeyPath);
        } catch (Exception e) {
     
            logger.error("初始化公钥和私钥失败!", e);
            throw new RuntimeException();
        }
    }
    //getter setter..
}

UserClient:远程调用接口

@FeignClient("user-service")
public interface UserClient extends UserApi {
     
}

AuthController:

package com.leyou.auth.controller;

import com.leyou.auth.config.JwtProperties;
import com.leyou.auth.service.AuthService;
import com.leyou.common.pojo.UserInfo;
import com.leyou.common.utils.CookieUtils;
import com.leyou.common.utils.JwtUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @auther Mr.Liao
 * @date 2019/5/23 18:43
 */
@Controller
//启用配置类,读取配置文件的信息
@EnableConfigurationProperties(JwtProperties.class)
public class AuthController {
     
    @Autowired
    private AuthService authService;
    @Autowired
    private JwtProperties jwtProperties;
    /**
     * 登录并将token写入到cookie中
     * @param username
     * @param password
     * @param request
     * @param response
     * @return
     */
    @PostMapping("login")
    public ResponseEntity<Void> accredit(@RequestParam("username")String username,
                                         @RequestParam("password")String password,
                                         HttpServletRequest request,
                                         HttpServletResponse response){
     
        String token = this.authService.accredit(username, password);
        if (StringUtils.isBlank(token)){
     
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }
        // 将token写入Cookie
        CookieUtils.setCookie(request,response,this.jwtProperties.getCookieName(),token,this.jwtProperties.getExpire() * 60);
        return ResponseEntity.ok(null);
    }
    /**
     * 验证用户是否登陆,返回user对象,更新token
     */
    @GetMapping("verify")
    public ResponseEntity<UserInfo> verify(@CookieValue("LY_TOKEN")String token, HttpServletRequest request, HttpServletResponse response){
     
        try {
     
            // 使用JwtUtils解析Cookie中的token,获取用户信息
            UserInfo user = JwtUtils.getInfoFromToken(token, this.jwtProperties.getPublicKey());
            // 未登录,返回401 未授权,状态码
            if (user == null){
     
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
            }
            //已登录,生成新的token,加入到Cookie中
            token = JwtUtils.generateToken(user, this.jwtProperties.getPrivateKey(), this.jwtProperties.getExpire());
            CookieUtils.setCookie(request,response,this.jwtProperties.getCookieName(),token,this.jwtProperties.getExpire()*60);
            return ResponseEntity.ok(user);
        } catch (Exception e) {
     
            e.printStackTrace();
        }
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
    }
}

AuthService:

package com.leyou.auth.service;

import com.leyou.auth.client.UserClient;
import com.leyou.auth.config.JwtProperties;
import com.leyou.common.pojo.UserInfo;
import com.leyou.common.utils.JwtUtils;
import com.leyou.user.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @auther Mr.Liao
 * @date 2019/5/23 18:45
 */
@Service
public class AuthService {
     
    @Autowired
    private UserClient userClient;
    @Autowired
    private JwtProperties jwtProperties;

    public String accredit(String username, String password) {
     
        // 根据用户名和密码查询,这里在user-service服务查询user为null时,返回400状态码,feign报异常,所以要try
        User user;
        try {
     
            user = this.userClient.queryUser(username, password);
        } catch (Exception e) {
     
            e.printStackTrace();
            System.out.println("feign远程调用user-service查询出现错误");
            return null;
        }
        // 判断user是否为null
        if (user == null){
     
            return null;
        } else {
     
            // jwtUtils通过私钥生成token
            try {
     
                // 载荷
                UserInfo userInfo = new UserInfo();
                userInfo.setId(user.getId());
                userInfo.setUsername(user.getUsername());
                String token = JwtUtils.generateToken(userInfo, this.jwtProperties.getPrivateKey(), this.jwtProperties.getExpire());
                return token;
            } catch (Exception e) {
     
                e.printStackTrace();
                return null;
            }
        }

    }
}

启动类:

@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
public class LeyouAuthApplication {
     
    public static void main(String[] args) {
     
        SpringApplication.run(LeyouAuthApplication.class, args);
    }
}

解决cookie写入问题
由于我们配置了域名的映射
五、基于jwt token的无状态登陆_第3张图片
在nginx中配置
五、基于jwt token的无状态登陆_第4张图片
同时在zuul网关中配置

zuul:
  prefix: /api
  routes:
    item-service: /item/**     #路由到后台商品的微服务
    search-service: /search/** #路由到前台搜索微服务
    user-service: /user/**     #路由到用户微服务
    auth-service: /auth/**     #路由到授权微服务
    cart-service: /cart/**     #路由到购物车微服务
    order-service: /order/**   #路由到订单车微服务
  add-host-header: true #携带头信息(域名)
  sensitive-headers:    #值为空覆盖敏感默认头信息,能够将cookie设置到响应头
四、Zuul网关统一拦截

判断用户是否登陆,从cookie中获取token,然后通过公钥解析token,判断token是否有效。同时要判断哪些请求需要验证用户是否登陆,比如加入购物车或者关于订单的请求都要验证是否登陆,在配置中配置那些不需要验证的请求路径信息,配置文件如下:

server:
  port: 10010
spring:
  application:
    name: leyou-gateway
eureka:
  client:
    registry-fetch-interval-seconds: 5 # 拉取服务的时间
    service-url:
      defaultZone: http://localhost:10086/eureka
zuul:
  prefix: /api
  routes:
    item-service: /item/**     #路由到后台商品的微服务
    search-service: /search/** #路由到前台搜索微服务
    user-service: /user/**     #路由到用户微服务
    auth-service: /auth/**     #路由到授权微服务
    cart-service: /cart/**     #路由到购物车微服务
    order-service: /order/**   #路由到订单车微服务
  add-host-header: true #携带头信息(域名)
  sensitive-headers:    #值为空覆盖敏感默认头信息,能够将cookie设置到响应头
leyou:
  jwt:
    pubKeyPath: D:\IdeaProjects\springCloud\tmp\rsa\rsa.pub # 公钥地址
    cookieName: LY_TOKEN
  filter:
    allowPaths:
    - /api/auth
    - /api/search
    - /api/user/register
    - /api/user/check
    - /api/user/code
    - /api/item

通过两个配置属性类来获取配置属性
JwtProperties

package com.leyou.gateway.config;

import com.leyou.common.utils.RsaUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;

import javax.annotation.PostConstruct;
import java.security.PublicKey;

@ConfigurationProperties(prefix = "leyou.jwt")
public class JwtProperties {
     

    private String pubKeyPath;   // 公钥路径
    private PublicKey publicKey; // 公钥
    private String cookieName;   // cookie名字
    private static final Logger logger = LoggerFactory.getLogger(JwtProperties.class);

    /**
     * @PostContruct:在构造方法执行之后执行该方法
     */
    @PostConstruct
    public void init(){
     
        try {
     
            // 获取公钥
            this.publicKey = RsaUtils.getPublicKey(pubKeyPath);
        } catch (Exception e) {
     
            logger.error("初始化公钥和私钥失败!", e);
            throw new RuntimeException();
        }
    }
    //getter setter
}

FilterProperties

@ConfigurationProperties(prefix = "leyou.filter")
public class FilterProperties {
     

    private List<String> allowPaths;

    public List<String> getAllowPaths() {
     
        return allowPaths;
    }
    public void setAllowPaths(List<String> allowPaths) {
     
        this.allowPaths = allowPaths;
    }
}

LoginFilter:过滤器

package com.leyou.gateway.filter;

import com.leyou.common.utils.CookieUtils;
import com.leyou.common.utils.JwtUtils;
import com.leyou.gateway.config.FilterProperties;
import com.leyou.gateway.config.JwtProperties;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * @auther Mr.Liao
 * @date 2019/5/23 20:45
 */
@Component
@EnableConfigurationProperties({
     JwtProperties.class, FilterProperties.class})
public class LoginFilter extends ZuulFilter {
     

    @Autowired
    private JwtProperties jwtProperties;

    @Autowired
    private FilterProperties filterProperties;
	
	//前置过滤器,在路由之前过滤
    @Override
    public String filterType() {
     
        return "pre";
    }
	//过滤器执行的顺序,级别
    @Override
    public int filterOrder() {
     
        return 10;
    }

    // return true 执行run方法,在白名单中就不进行拦截
    @Override
    public boolean shouldFilter() {
     
        // 获取白名单
        List<String> allowPaths = this.filterProperties.getAllowPaths();
        // 初始化运行上下文
        RequestContext context = RequestContext.getCurrentContext();
        // 获取request对象
        HttpServletRequest request = context.getRequest();
        String url = request.getRequestURI();
        for (String allowPath : allowPaths) {
     
            if (StringUtils.contains(url, allowPath)){
     
                return false;
            }
        }
        return true;
    }

    @Override
    public Object run() throws ZuulException {
     
        // 初始化运行上下文
        RequestContext context = RequestContext.getCurrentContext();
        // 获取request对象
        HttpServletRequest request = context.getRequest();
        //获取token
        String token = CookieUtils.getCookieValue(request, this.jwtProperties.getCookieName());

        try {
     
            JwtUtils.getInfoFromToken(token,this.jwtProperties.getPublicKey());
        } catch (Exception e) {
     
            //如果解析出现异常,则说明token无效,没有token时也会出现异常
            e.printStackTrace();
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        return null;
    }
}

你可能感兴趣的:(某商城项目)