完成单点登录
編寫登錄接口
接下來,我們需要在auth編寫一個接口,對外提供登錄授權服務。基本流程如下:
-
客戶端攜帶用戶名和密碼請求登錄
-
授權中心調用用戶中心接口,根據用戶名和密碼查詢用戶信息
-
如果用戶名密碼正確,能獲取用戶,否則登錄失敗
-
如果校驗成功,則生成JWT并返回
編寫遠程調用接口
創建ums-interface工程:
pom.xml中的依賴,參照其他interface工程。并在ums和auth工程中引入該接口工程
UmsApi:
public interface UmsApi {/*** 根據用戶名和密碼查詢用戶* @param username* @param password* @return*/@GetMapping("ums/member/query")public Resp<MemberEntity> queryUser(@RequestParam("username") String username,@RequestParam("password") String password); }生成公鑰和私鑰
我們需要在授權中心生成真正的公鑰和私鑰。我們必須有一個生成公鑰和私鑰的secret,這個可以配置到application.yml中:
jwt:pubKeyPath: C:\\tmp\\rsa\\rsa.pub # 公鑰地址priKeyPath: C:\\tmp\\rsa\\rsa.pri # 私鑰地址secret: sf3423jsdf#3$@FDS32expire: 30 # 過期時間,單位分鐘cookieName: TOKEN然后編寫屬性類讀取jwt配置,并從秘鑰配置文件中讀取出響應的公鑰及私鑰,加載這些數據:
@Data @Slf4j @ConfigurationProperties(prefix = "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名稱/*** @PostContruct:在構造方法執行之后執行該方法*/@PostConstructpublic 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) {log.error("初始化公鑰和私鑰失敗!", e);throw new RuntimeException();}} }AuthController
編寫授權接口,我們接收用戶名和密碼,校驗成功后,寫入cookie中。
-
請求方式:post
-
請求路徑:/auth/accredit
-
請求參數:username和password
-
返回結果:無
代碼:
@RestController @RequestMapping("auth") @EnableConfigurationProperties(JwtProperties.class) public class AuthController {@Autowiredprivate AuthService authService;@Autowiredprivate JwtProperties jwtProperties;/*** 登錄授權** @param username* @param password* @return*/@PostMapping("accredit")public Resp<Object> authentication(@RequestParam("username") String username,@RequestParam("password") String password,HttpServletRequest request,HttpServletResponse response) {// 登錄校驗String token = this.authService.authentication(username, password);if (StringUtils.isBlank(token)) {return Resp.fail("登錄失敗,用戶名或密碼錯誤");}// 將token寫入cookie,并指定httpOnly為true,防止通過JS獲取和修改CookieUtils.setCookie(request, response, jwtProperties.getCookieName(), token, jwtProperties.getExpire());return Resp.ok("登錄成功");} }AuthService
在auth-service:
@Service public class AuthService {@Autowiredprivate UmsClient umsClient;@Autowiredprivate JwtProperties jwtProperties;public String authentication(String username, String password) {try {// 調用微服務,執行查詢Resp<MemberEntity> resp = this.umsClient.queryUser(username, password);MemberEntity memberEntity = resp.getData();// 如果查詢結果為null,則直接返回nullif (memberEntity == null) {return null;}// 如果有查詢結果,則生成tokenMap<String, Object> map = new HashMap<>();map.put("id", memberEntity.getId());map.put("username", memberEntity.getUsername());String token = JwtUtils.generateToken(map, jwtProperties.getPrivateKey(), jwtProperties.getExpire());return token;} catch (Exception e) {e.printStackTrace();}return null;} }UmsClient
接下來我們肯定要對用戶密碼進行校驗,所以我們需要通過FeignClient去訪問 ums-service微服務:
在auth中引入ums-interface依賴:
<dependency><groupId>com.atguigu</groupId><artifactId>ums-interface</artifactId><version>0.0.1-SNAPSHOT</version> </dependency>編寫UmsClient:
@FeignClient("ums-service") public interface UmsClient extends UmsApi { }CookieUtils
要注意,這里我們使用了一個工具類,CookieUtils,可以在課前資料中找到,我們把它添加到core中,然后引入servlet相關依賴即可:
package com.leon.core.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)// 設置域名的cookiecookie.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.cndomainName = domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];} else if (len <= 3 && len > 1) {// xxx.com or xxx.cndomainName = domains[len - 2] + "." + domains[len - 1];} else {domainName = serverName;}}if (domainName != null && domainName.indexOf(":") > 0) {String[] ary = domainName.split("\\:");domainName = ary[0];}return domainName;}}?
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
- 上一篇: jwt工具类介绍
- 下一篇: 编写网关过滤器统一校验登录状态