新四季網

微服務spring cloud 子項目(微服務場景實戰)

2023-04-16 20:48:09 1

鑑權中心服務認識JWT

json web token 是一個開放的標準 ,它定義了一個種緊湊的,自包含的方式,用於作為json對象在各方之間安全的傳輸信息

jwt

JWT組成部分

Header :頭部信息

Header 由兩部分組成(Token類型,加密算法的名稱),並且使用的是base64的編碼

Payload: 我們想要傳遞的數據

Payload KV形式的詩數據 ,這裡就是我們想要傳遞的信息(授權的話就是Token信息)

Signature :籤名

Signature 為了得到籤名 首先我們得有編碼過的Header 編碼過的payload 和一個密鑰。籤名用的算法就是header中指定的那個,之後就會對他們籤名

我們需要一個籤名公式

HMACSHA245(base64UrlEncode(header) "." base64UrlEncode(payload),secret)

產生一個籤名,返回一個字符串,返回給客戶端,之後客戶端每次訪問都要帶上這個字符串,進行鑑權

JWT 使用 . 號來連接 HHH.PPPP.SSSS

授權,鑑權設計

這裡我們先不考慮 gateway 網關,後續會搭建,我們的重點放在中間和右邊部分

鑑權部分,我們獨立實現公共的工具類,為什麼?以下三點

JWT本質上是通過算法算出的加密字符串,也可以通過算法反向解析出來,他不依賴任何的框架,所以這個功能有可以單獨提取出來的前提我們的電商系統包含多個微服務,很顯然我們每個服務都需要鑑權,於是我們把這個方法提取出來,方便復用高性能鑑權,為什麼不在授權中心做鑑權,首先他回頭過http請求等一系列操作,我們在本地只用java的話 少去了很多步驟,性能得到倍數的增長授權編碼實現

我們創建新的一個服務來編寫我們的鑑權中心

e-commerce-authority-center

導入相關的依賴

com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery 2.2.3.RELEASE org.springframework.boot spring-boot-starter-data-Jpa mysql mysql-connector-java 8.0.12 runtime com.hyc.ecommerce e-commerce-mvc-config 1.0-SNAPSHOT org.springframework.cloud spring-cloud-starter-zipkin org.springframework.kafka spring-kafka 2.5.0.RELEASE org.freemarker freemarker 2.3.30 cn.smallbun.screw screw-core 1.0.3

導入好依賴之後我們 編寫對應的配置,如註冊到naocs 加入adminserver的監管,配置數據源等 這裡我們使用jpa 來做orm

配置編寫

server: port: 7000 servlet: context-path: /ecommerce-authority-centerspring: application: name: e-commerce-authority-center cloud: nacos: discovery: enabled: true # 如果不想使用 Nacos 進行服務註冊和發現, 設置為 false 即可 server-addr: 127.0.0.1:8848 # Nacos 伺服器地址 # server-addr: 127.0.0.1:8848,127.0.0.1:8849,127.0.0.1:8850 # Nacos 伺服器地址 namespace: 1bc13fd5-843b-4ac0-aa55-695c25bc0ac6 metadata: management: context-path: ${ server.servlet.context-path}/actuator jpa: show-sql: true hibernate: ddl-auto: none properties: hibernate.show_sql: true hibernate.format_sql: true open-in-view: false datasource: # 數據源 url: jdbc:mysql://127.0.0.1:3306/imooc_e_commerce?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC username: root password: root type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver # 連接池 hikari: maximum-pool-size: 8 minimum-idle: 4 idle-timeout: 30000 connection-timeout: 30000 max-lifetime: 45000 auto-commit: true pool-name: ImoocEcommerceHikariCP kafka: bootstrap-servers: 127.0.0.1:9092 producer: retries: 3 consumer: auto-offset-reset: latest zipkin: sender: type: kafka # 默認是 web base-url: http://127.0.0.1:9411/# 暴露端點management: endpoints: web: exposure: include: '*' endpoint: health: show-details: always

配置完成之後,編寫主啟動類 @EnableJpaAuditing 因為我們用到 自動加入創建時間和修改時間,所以我們需要打開 jpa的自動審計功能,不然會報錯

@EnableJpaAuditing //允許 jpa 的自動審計@SpringBootApplication@EnableDiscoveryClientpublic class AuthorityApplication { public static void main(String[] args) { SpringApplication.run(AuthorityApplication.class, args); }}

test包下就測試環境是否正確

/** * 授權中心測試入口 * 驗證授權中心 環境可用性 */@SpringBootTest@RunWith(SpringRunner.class)public class AuthorityCenterApplicationTest { @Test public void conetextLoad { }}

環境ok之後

我們去測試 資料庫操作是否可用

編寫實體類 ecommerceUser

/* * 用戶表實體類定義 * */@Entity@EntityListeners(AuditingEntityListener.class)@Table(name = "t_ecommerce_user")@Data@NoArgsConstructor@AllArgsConstructorpublic class EcommerceUser { /* 自增組件*/ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private long id; /*用戶名*/ @Column(name = "username", nullable = false) Private String username; /* MD5 密碼*/ @Column(name = "password", nullable = false) private String password; /*額外的信息 json 字符串存儲*/ @Column(name = "extra_info", nullable = false) private String extraInfo; /*自動加入創建時間 需要主啟動類的註解*/ @CreatedDate @Column(name = "create_time", nullable = false) private Date createTime; /*自動加入更新時間 需要主啟動類的註解*/ @CreatedDate @Column(name = "update_time", nullable = false) private Date updateTime;}

有了實體類我們需要有數據操作的實現 於是編寫Dao 接口

其實當我們創建接口的時候jpa就已經有了對應的基礎增刪改查的方法

這裡我們實現兩個自定義查詢方法

/** * EcommerceUserDao 接口定義 */public interface EcommerceUserDao extends JpaRepository { /* * 根據用戶名查詢 EcommerceUser 對象 * 等於 select * form t_ecommerce_user where username=? * */ EcommerceUser findByUsername(String name); /* * 根據用戶名查詢 EcommerceUser 對象 * 等於 select * form t_ecommerce_user where username=? and password=? * */ EcommerceUser findByUsernameAndPassword(String name, String password);}

之後創建 test service

/** * @author : 冷環淵 * @date : 2021/12/4 * @context: EcommerceUser 相關測試 * @params : null * @return : * @return : null */@SpringBootTest@RunWith(SpringRunner.class)@Slf4jpublic class EcommerUserTest { @Autowired EcommerceUserDao ecommerceUserDao; /*測試 新增一個用戶數據 */ @Test public void createUserRecord { EcommerceUser ecommerceUser = new EcommerceUser; //設置要插入的信息 ecommerceUser.setUsername("[email protected]"); ecommerceUser.setPassword(MD5.create.digestHex("123456")); ecommerceUser.setExtraInfo("{}"); //日誌列印返回結果 log.info("server user:[{}]", JSON.toJSON(ecommerceUserDao.save(ecommerceUser))); } /*測試 我們編寫的自定義方法 查詢 剛才創建的新角色*/ @Test public void SelectUserInfo { String username = "[email protected]"; log.info("select userinof:[{}]", JSON.toJSON(ecommerceUserDao.findByUsername(username))); }}

測試相關的 方法 新增用戶啊 或者是 按條件查詢用戶 ,測試均通過

生成RSA256的公鑰 和 私鑰 非對稱加密算法

他通過 私鑰加密 公鑰解密來完成驗證,目前很多的鑑權 都是 JWT RSA256的算法來加密鑑權的,如果了解不多,就是用RSA256就可以了

編碼

編寫生成公鑰密鑰的測試類,創建 一些我們常用的 VO 對象 用來儲存我們常用的一些變量,比如 用戶信息 , 公鑰 , 密鑰 ,一些常用的屬性 放進 VO的模型裡

@Slf4j@SpringBootTest@RunWith(SpringRunner.class)/** * * @author : 冷環淵 * @date : 2021/12/5 * @context: RSA 非對稱 加密算法 * @params : null * @return : * @return : null */public class RSATest { @Test public void generateKeyBytes throws Exception { /*獲取到 RSA算法實例*/ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); /* 這裡最小是 2048 低於的話 是會報錯的*/ keyPairGenerator.initialize(2048); /* * 生成公鑰對 * */ KeyPair keyPair = keyPairGenerator.generateKeyPair; /*獲取 公鑰和私鑰對象*/ RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic; RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate; log.info("private key:[{}]", Base64.encode(privateKey.getEncoded)); log.info("public key:[{}]", Base64.encode(publicKey.getEncoded)); }}

創建VO對象保存 我們常用且不會變化的值和對象

存儲私鑰 應為是私鑰 所以只對鑑權中心 暴露 於是我們在鑑權服務中創建 Constant包 創建這個 AuthotityConstant 類保存信息

/** * @author : 冷環淵 * @date : 2021/12/5 * @context: 鑑權的常量 * @params : null * @return : * @return : null */public class AuthorCanstant { /*私鑰 只暴露給 鑑權中心 不暴露給任何的其他服務*/ public static final String PRIVATE_KEY = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBA" "QCMXrQCudalKHJlH16YHr9mI5/xyYnkp5u2gAbMFf2xAHAyykYmixJP3CqG2a8tUwiJjjTIJXP 79Jzgjgg" "VbBaTakrvjeFXz9HNP1D4XD6Li sRVjnN1iBUwIFRxiFN2EOJflA9bqeQLAge/LgAu06y3jdLLleJF7yDRuMH" "YedqPl9AJa5RdJmt0OgCoVOqacB7oGkFCFISm0Cwjfgq06nyiiULGZNVt8uhDxZAE4Pi2lmf3yggXCBH9AtU/2" "XdyxU9caQJOAbYGxd/mART/NivBjSqo60wcBnktI booUbDKRBbWRxvfYqKWEwPOwxlJUB3l3pcLZm866Xl3qtVM" "XAgMBAAECggEADCGjLRkik OK/3JWmo8Nu6YYjKz XeSecIdgDwNXiZSgHcOdjHc4fe5pPn5RxXkHo9vGdAXIoJ/Z" "cGIwt5qwQx2zITSvV7eDoIPT36n8OaMEO79Cj7kYzRR/eDVMyTagDLj7ccHK/yJYFnaf5vxZxFsRdwwGeTxreD" "/pwZJLxjRSz1W57v5yUJNPPimNB229EogNYHIhQ8 Z7OGiilbtBIL9r6lqlz2hUAVBzXl4kOXFVI vEodLuV2" "rtQXXrpO1 AgH5lZJ7ahShKbqHt/Q6uJSTKAhbsfv/iadcPjmYp2F7nnYBLf66Jln6AWUwnXrJ7XETOf/ Qcib" "q/5m6RjAQKBgQDruxn kaDr5uYQMVSHog CBRBJghJ4JklhY7ZDYJ2wN2KNHOd3mW/wUVDihVIyRFniIzsWU" "0lnI 4OLqNLAZOBaQB5VrjyH4fxn5b26t0xLO1d5EWcOYI8ZRhwWDWaZipe2dUMeqVVMYFeDdTdNsyGrf8x" "L OVyRDiH4s4pBIs7QKBgQCYcIVFgDbrmwsP7lA9/dU9kClutY3gjEUgB2IJp2Y8S4Xhfi4NC8GqRQoMUyuqg" "vPHKEiTCa1EojGHS/ r4JVcSg9Wsv64SpGZ gANxRhfYFPrbkjU4YOMaZeCGUfKR2QnD20c3I4gdQ9kU5nK52n Y" "JEkAFUejg1Mhb6Fp6HDkwKBgAHYYBa3CxxtnUVpLXE2Woq5AWyh4QUhv5dMkYOrgPB9Ln9OR52PDOpDqK9tP" "bx4/n8fqXm QyfUhyuDP/H5XC86JC/O9vmmN4kzp5ndMsgMwvrmK4lShet1GyDd/ VqgVBmwh0r5JlrHske" "sJjesfEn8YRwDIcCoOg0OQHDfwTtAoGAQfE61YvXNihFqsiOkaKCYjVAlxGWpDJJnMdU05REl4ScD6WDy" "kTxq/RdmmNIGmS3i8mTS3f Khh3kG2B1ho6wkePRxP7OEGZpqAM8ef22RtUch2tB9neDBmJXtAMzCYB3xu/O" "aL3IHdDB0Va2/krUsz3PDmgmK0ed6HLfwm64l0CgYB iGkMAQEwqYmcCEXKK825Q9y/u8PE9y8uaMGfsZQzDo6v" "V5v reOhmZRrk5BnX pgztbE28sS6c2vYR0RYoR90aD2GXungCPXWEMDQudHFxvSsNTCYkDynjTSlnzu9aDcfqw1" "UIzHog2zCquSro7tnbOMsvV5UdsLBq WNQGgAw=="; /*默認的 token 超時時間,一天*/ public static final Integer DEFAULT_EXPIRE_DAY = 1;}

之後是創建一些公共常用的VO模型 e-commerce-common

保存 公鑰到公用包 以後我們的服務 需要做授權都需要使用到

/** * @author : 冷環淵 * @date : 2021/12/5 * @context: 通用模塊的常量定義 * @params : null * @return : * @return : null */public class CommonCanstant { /* RSA 公鑰*/ public static final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjF60ArnWpShyZ" "R9emB6/ZiOf8cmJ5KebtoAGzBX9sQBwMspGJosST9wqhtmvLVMIiY40yCVz/u/Sc4I4IFWwWk2pK743hV8/RzT9Q F" "w i4vrEVY5zdYgVMCBUcYhTdhDiX5QPW6nkCwIHvy4ALtOst43Sy5XiRe8g0bjB2Hnaj5fQCWuUXSZrdDoAqFTqmnA" "e6BpBQhSEptAsI34KtOp8oolCxmTVbfLoQ8WQBOD4tpZn98oIFwgR/QLVP9l3csVPXGkCTgG2BsXf5gEU/zYrwY0qqO" "tMHAZ5LSPm6KFGwykQW1kcb32KilhMDzsMZSVAd5d6XC2ZvOul5d6rVTFwIDAQAB"; /* JWT 中 存儲用戶信息到 key*/ public static final String JWT_USER_INFO_KEY = "e-commerce-user"; /*授權中心的 service-id*/ public static final String AUTHORITY_CENTER_SERVICE_ID = "e-commerce-authity-center";}

用戶信息的常用VO對象

JwtTokenLoginUserinfoUsernameAndPassword

/** * @author : 冷環淵 * @date : 2021/12/5 * @context: 授權中心 鑑權 之後給客戶端的token * @params : null * @return : * @return : null */@Data@NoArgsConstructor@AllArgsConstructorpublic class JwtToken { /* JWT*/ private String token;}

@Data@NoArgsConstructor@AllArgsConstructorpublic class LoginUserinfo { /*用戶 id*/ private Long id; /*用戶名*/ private String username;}

/** * @author : 冷環淵 * @date : 2021/12/5 * @context:用戶名和密碼 * @params : null * @return : * @return : null */@Data@AllArgsConstructor@NoArgsConstructorpublic class UsernameAndPassword { /*用戶名 */ private String username; /*密碼*/ private String password;}

授權服務編寫

首先創建一個 接口 IJWTService

定義我們需要實現的授權方法

/** * @author : 冷環淵 * @date : 2021/12/5 * @context: JWT 相關服務接口定義 * @params : null * @return : * @return : null */public interface IJWTService { /* * 生成 token 使用默認的超時時間 * */ String generateToken(String username, String password) throws Exception; /* * 生成 JWT Token 可以設置超時時間 單位是天 * */ String generateToken(String username, String password, Integer expireTime) throws Exception; /* * 註冊用戶並且生成 token 返回 * */ String registerUserAndGenerateToken(UsernameAndPassword usernameAndPassword) throws Exception;}

授權方法實現類

這裡我們有三個方法實現

默認超時時間的 生成 token自定義超時時間的設置生成token註冊新用戶並且生成的token返回

JWT對象生成細節:

1) 我們需要設置需要傳遞的對象

2)我們需要設置一個不重複的 id

3)我們需要設置超時時間

4)設置我們的加密籤名

5)完成設置返回字符串對象

Jwts.builder //這裡 claim 其實就是 jwt 的 payload 對象 --> KV .claim(CommonCanstant.JWT_USER_INFO_KEY, JSON.toJSONString(loginUserinfo)) // jwt id 表示是 jwt的id .setId(UUID.randomUUID.toString) //jwt 的過期時間 .setExpiration(expireDate) // 這裡是設置加密的私鑰和加密類型 .signWith(getPrivateKey, SignatureAlgorithm.RS256) //生成 jwt信息 返回的是一個字符串類型 .compact; }

完整代碼

@Service@Slf4j@Transactional(rollbackFor = Exception.class)public class IJWTServiceIpml implements IJWTService { @Autowired private EcommerceUserDao ecommerceUserDao; @Override public String generateToken(String username, String password) throws Exception { return generateToken(username, password, 0); } @Override public String generateToken(String username, String password, Integer expireTime) throws Exception { //首先需要驗證用戶是否通過授權校驗,即 輸入的用戶名和密碼能否尋找到匹配數據表的記錄 EcommerceUser ecommerceUser = ecommerceUserDao.findByUsernameAndPassword(username, password); if (ecommerceUser == null) { log.error("can not find user:[{}],[{}]", username, password); return null; } //Token 中塞入對象, 即 JWT中 儲存的對象,後端拿到這些信息 就可以知道那個用戶在操作 LoginUserinfo loginUserinfo = new LoginUserinfo( ecommerceUser.getId, ecommerceUser.getUsername ); if (expireTime KV .claim(CommonCanstant.JWT_USER_INFO_KEY, JSON.toJSONString(loginUserinfo)) // jwt id 表示是 jwt的id .setId(UUID.randomUUID.toString) //jwt 的過期時間 .setExpiration(expireDate) // 這裡是設置加密的私鑰和加密類型 .signWith(getPrivateKey, SignatureAlgorithm.RS256) //生成 jwt信息 返回的是一個字符串類型 .compact; } @Override public String registerUserAndGenerateToken(UsernameAndPassword usernameAndPassword) throws Exception { //先去校驗 用戶名是否存在 如果存在 不能重複註冊 EcommerceUser oldUser = ecommerceUserDao.findByUsername(usernameAndPassword.getUsername); if (null != oldUser) { log.error("username is registered:[{}]", oldUser.getUsername); return null; } EcommerceUser ecommerceUser = new EcommerceUser; ecommerceUser.setUsername(usernameAndPassword.getUsername); ecommerceUser.setPassword(usernameAndPassword.getPassword); //MD5 編碼以後 ecommerceUser.setExtraInfo("{}"); //註冊一個新用戶 寫到一個 記錄表中 ecommerceUser = ecommerceUserDao.save(ecommerceUser); log.info("regiter user success:[{}],[{}]", ecommerceUser.getUsername); //生成 token 並且返回 return generateToken(ecommerceUser.getUsername, ecommerceUser.getPassword); } /* * 根據本地儲存的私鑰獲取到 PrivateKey對象 * */ private PrivateKey getPrivateKey throws Exception { //使用給定的編碼密鑰創建一個新的PKCS8EncodedKeySpec。 PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(new BASE64Decoder.decodeBuffer(AuthorCanstant.PRIVATE_KEY)); // 設置生成新密鑰的工廠加密方式 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); //返回生成好的密鑰 return keyFactory.generatePrivate(priPKCS8); }}

之後我們的授權都會使用到以上的方法

Controller

我們需要給註冊用戶和生成token 一個程序的入口

就是我們的 AuthorityController ,這裡可以用到我們之前使用的註解 @IgnoreResponseAdvice 我們為啥那麼不讓他封裝呢,我們需要驗證,單純的 JwtToken 對象就可以了,不需要封裝和轉化

@Slf4j@RestController@RequestMapping("/authority")public class AuthorityConroller { private final IJWTService ljwtService; public AuthorityConroller(IJWTService ljwtService) { this.ljwtService = ljwtService; } /* * 從授權中心 獲取 token (其實就是登陸功能) 且返回信息中沒有統一響應的包裝 * */ @IgnoreResponseAdvice @PostMapping("/token") public JwtToken token(@RequestBody UsernameAndPassword usernameAndPassword) throws Exception { //通常 日誌裡不會答列印用戶的信息 防止洩露,我們這本身就是一個授權伺服器,本身就不對外開放,所以我們可以列印用戶信息到日誌方便查看 log.info("request to get token with param:[{}]", JSON.toJSONString(usernameAndPassword)); return new JwtToken(ljwtService.generateToken( usernameAndPassword.getUsername, usernameAndPassword.getPassword)); } /*註冊用戶並且返回註冊當前用戶的token 就是通過授權中心常見用戶*/ @IgnoreResponseAdvice @PostMapping("/register") public JwtToken register(@RequestBody UsernameAndPassword usernameAndPassword) throws Exception { log.info("register user with param:[{}]", JSON.toJSONString(usernameAndPassword)); return new JwtToken(ljwtService.registerUserAndGenerateToken(usernameAndPassword)); }}

鑑權編碼實現

這裡我們打鑑權 放到公共模塊裡 為什麼呢,這裡我們不止是鑑權中心還有其他的服務也要用到鑑權服務,秉著封裝的思想,我們提取公共的方法放到 Common 裡面

創建JWT Token解析類 TokenParseUtil

/** * @author : 冷環淵 * @date : 2021/12/5 * @context: JWT Token 解析工具類 * @params : null * @return : * @return : null */public class TokenParseUtil { public static LoginUserinfo parseUserInfoFromToken(String token) throws Exception { if (null == token) { return null; } Jws claimsJws = parseToken(token, getPublicKey); Claims body = claimsJws.getBody; //如果 Token 已經過期返回 null if (body.getExpiration.before(Calendar.getInstance.getTime)) { return null; } // 返回 Token中保存的用戶信息 return JSON.parseObject( body.get(CommonCanstant.JWT_USER_INFO_KEY).toString, LoginUserinfo.class ); } /* * 通過公鑰去解析 JWT Token * */ private static Jws parseToken(String token, PublicKey publicKey) { // 用設置籤名公鑰,解析claims信息 token return Jwts.parser.setSigningKey(publicKey).parseClaimsJws(token); } /* * 根據本地存儲的公鑰獲取到 getPublicKey * */ public static PublicKey getPublicKey throws Exception { //解碼器 我們設置解碼器 將公鑰放進去 X509EncodedKeySpec keySpec = new X509EncodedKeySpec( new BASE64Decoder.decodeBuffer(CommonCanstant.PUBLIC_KEY) ); //創建 RSA 實例 通過示例生成公鑰對象 return KeyFactory.getInstance("RSA").generatePublic(keySpec); }}

這裡是涉及到一個問題 ,token要是傳輸的不是jwt token對象,會跑出異常,沒有兜底,

其實這裡這問題其實也不成立,應為你沒有傳入token對象,我們這裡拋出異常是正確的,也不會影響其他服務,之後搭配sentinel和豪豬哥 可以實現異常重啟等等,這裡我們就先不編寫兜底方法,以解析jwt token為主。

驗證鑑權授權

我們寫一個 test 類來測試 授權和鑑權拿到對象,是否有效

/** * @author : 冷環淵 * @date : 2021/12/5 * @context: JWT 相關測試類 * @params : null * @return : * @return : null */@Slf4j@SpringBootTest@RunWith(SpringRunner.class)public class JWTServiceTest { @Autowired private IJWTService ijwtService; @Test public void testGenerateAndParseToken throws Exception { String jwtToken = ijwtService.generateToken( "[email protected]", "e10adc3949ba59abbe56e057f20f883e" ); log.info("jwt token is:[{}]", jwtToken); LoginUserinfo userinfo = TokenParseUtil.parseUserInfoFromToken(jwtToken); log.info("userinfo by jwt prase token :[{}]", JSON.toJSONString(userinfo)); }}

啟動測試查看結果

eyJhbGciOiJSUzI1NiJ9.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjExLFwidXNlcm5hbWVcIjpcImh5Y0BxcS5jb21cIn0iLCJqdGkiOiIzNDgwNjdjMi00MTBlLTQ3MjItYmM3ZS02NWQyYmNmYTRkN2MiLCJleHAiOjE2Mzg3MjAwMDB9.ZbFl81MkIipJSULZLf4F2X2Fb0q1TwhHIMT7nyZsZVwUxXyZnK54RlzoGM_b-kMUdKO_Tab-qEeOT6Jn--FiKmbOziWXiBx3a-k5ipthMJx0Fez-X8Acty-Pg7zukNalugiLxGb5ophQoVQWRTDmv2hytGHqiV71HVyErznkJa36QQr6QsjXqlJleo3BBt-6BFzdTFPLUmdTEJ4XsmZBa_acUDGBhY0_tU2gYtKBWhwvMCknuyCcV-_GVI5EvgMIKRpeFSZrWfTsDG2y1MFcyzjKE6jnzek-YwT3XkzQ8eGzUbiOlaU_Zx5OJah-UtrKwqlAw9WbO71pNgEBefdsYw

這是封裝好的 JWT Token 這裡我們可以看到三個點分別分割 了 header和payload以及籤名,和我們之前講的 結構一模一樣,

userinfo by jwt prase token :[{"id":11,"username":"[email protected]"}]

獲取到的我們放在 jwt 裡面需要傳遞的對象

驗證對外提供的接口是否好用

這裡我們編寫 http腳本來測試對外題提供的接口是否有用

Token 方法

### 獲取 Token -- 登錄功能實現POST http://127.0.0.1:7000/ecommerce-authority-center/authority/tokenContent-Type: application/json{ "username": "[email protected]", "password": "e10adc3949ba59abbe56e057f20f883e"}

POST http://127.0.0.1:7000/ecommerce-authority-center/authority/tokenHTTP/1.1 200 Content-Type: application/jsonTransfer-Encoding: chunkedDate: Sun, 05 Dec 2021 15:35:52 GMTKeep-Alive: timeout=60Connection: keep-alive{ "token": "eyJhbGciOiJSUzI1NiJ9.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjExLFwidXNlcm5hbWVcIjpcImh5Y0BxcS5jb21cIn0iLCJqdGkiOiIxNDU1M2FjZi1lZmE5LTQ4OTgtOTliYS1hNzA4NWI4MjU4MzAiLCJleHAiOjE2Mzg3MjAwMDB9.AlOpo6uf97R20ZLojXeun-3MK8DpSYlWxEygvDrtQeWaM9R0iKx-iW1VXnK6WoEntvqPxIrmPA7khjl3dXPa8kQHtdq-LVO7BDuZZDiQyZ64ZS7A9jWZr5JReSWBUSR1YUnsOvBRMkx4JVcAF3_W7nHwd722FFzOZRCr72hLHQIKpsugKtqjMEtaiEW0vcqphCYRJTAO_rQx1Lb1eVVg_Ufur0qSlKkV5dSJ0x3x9mc9UZRckwN0rrP7wQxZcrxJvKTfX7CkRRSO-CxZbG4WLokSaMtaGBMWU-7KGq7HSCZ0yuOgbbLdouHncsp6VD2tNLFdWSdJ_whCIbZxfX8R7w"}

獲取 token 成功

這裡他沒有被響應包裹,證明我們之前的選擇屏蔽註解也生效了,很符合我們的預期

驗證如果記錄數據表沒有是否會返回null

### 獲取 Token -- 登錄功能實現POST http://127.0.0.1:7000/ecommerce-authority-center/authority/tokenContent-Type: application/json### 隨便寫的id{"username": "[email protected]","password": "e10adc3949ba59abbe56e057f20f883e"}

返回結果 也符合我們預期 是 null

POST http://127.0.0.1:7000/ecommerce-authority-center/authority/tokenHTTP/1.1 200 Content-Type: application/jsonTransfer-Encoding: chunkedDate: Sun, 05 Dec 2021 15:40:44 GMTKeep-Alive: timeout=60Connection: keep-alive{ "token": null}

register

### 註冊用戶並返回 Token -- 註冊功能實現POST http://127.0.0.1:7000/ecommerce-authority-center/authority/registerContent-Type: application/json{ "username": "[email protected]", "password": "e10adc3949ba59abbe56e057f20f883e"}

這個用戶之前是註冊過的,我們來看一下是否會返回我們預期的處理

POST http://127.0.0.1:7000/ecommerce-authority-center/authority/registerHTTP/1.1 200 Content-Type: application/jsonTransfer-Encoding: chunkedDate: Sun, 05 Dec 2021 15:42:00 GMTKeep-Alive: timeout=60Connection: keep-alive{ "token": null}

現在我們去註冊一個新的用戶

### 註冊用戶並返回 Token -- 註冊功能實現POST http://127.0.0.1:7000/ecommerce-authority-center/authority/registerContent-Type: application/json{ "username": "[email protected]", "password": "e10adc3949ba59abbe56e057f20f883e"}

符合預期結果,創建了我們預期的對象,這個時候我們去看一下數據表

POST http://127.0.0.1:7000/ecommerce-authority-center/authority/registerHTTP/1.1 200 Content-Type: application/jsonTransfer-Encoding: chunkedDate: Sun, 05 Dec 2021 15:42:57 GMTKeep-Alive: timeout=60Connection: keep-alive{ "token": "eyJhbGciOiJSUzI1NiJ9.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjEyLFwidXNlcm5hbWVcIjpcImh5YzExQHFxLmNvbVwifSIsImp0aSI6IjMxNDc0NmIwLTMyOGYtNDZkNS05ZTIwLTg3YjI0OWY1ZjZkOCIsImV4cCI6MTYzODcyMDAwMH0.MKxk-Q4BG5kaYFAsLiy13trtk_gDFmCKORpdE4EAwgSVecXFQcYfT1VvqSAKvoQLFsSlQAxOR5elV8CFOoKwAomwqdyyghZp63NKJ2smRbg3Y-4jWBzFVsUgcjOY2fwh7oNTdHEsWmLBYAh5r0hm_MysZsUEsE-cwb3sw8NSMk1OZp0J6tcRras7V1Uw5xXH8OnCoq2cUfdynJMHS29EzJT1TFPb8unVQ_A1RWodsHdK3n1Bl4wFbJjMtnHx7vzOeAUSNJx1XpAGdo0xYHK6HBpS9E1KBS3x1AnYFONM0DKd4-_QxMkBW1kkg2uWrRpf3GYZF20FKxXgmBAPHGZhew"}

對象生成,功能驗證一切正常

鑑權服務中心總結

對比基於Token與基於伺服器的身份認證

傳統:最為傳統的做法,客戶端儲存 cookie 一般是 Session id 伺服器存儲 SessionSession 是每次用戶認證通過以後 ,伺服器需要創建一條記錄保存用戶信息,通常是在內存中(也可以放在redis中),隨著認證通過的用戶越來越多,伺服器的在這裡的開銷就會越來越大不同域名之前切換的時候,請求可能會被禁止,即跨越問題

基於tokenJWT與Session的差異相同點是,他們都是存儲用戶信息。然而Session是在伺服器端的,而JWT是在客戶端的JWT方式將用戶狀態分散到了客戶端中,可以明顯減輕請伺服器的內存壓力,服務端只需要用算法解析客戶端的token就可以得到信息

兩者優缺點的對比

解析方法:JWT使用算法直接解析得到用戶信息;Session需要額外的數據映射。實現匹配管理方法:JWT只有過期時間的限制,Session 數據保存在伺服器,可控性更強跨平臺:JWT就是一段字符串,可以任意傳播,Session跨平臺需要有統一的解析平臺,較為繁瑣時效性:JWT一旦生成 獨立存在,很難做到特殊的控制;Session時效性完全由服務端的邏輯說了算

TIPS :各自都有優缺點,都是登陸和授權的解決方案

,
同类文章
葬禮的夢想

葬禮的夢想

夢見葬禮,我得到了這個夢想,五個要素的五個要素,水火只好,主要名字在外面,職業生涯良好,一切都應該對待他人治療誠意,由於小,吉利的冬天夢想,秋天的夢是不吉利的
找到手機是什麼意思?

找到手機是什麼意思?

找到手機是什麼意思?五次選舉的五個要素是兩名士兵的跡象。與他溝通很好。這是非常財富,它擅長運作,職業是仙人的標誌。單身男人有這個夢想,主要生活可以有人幫忙
我不怎麼想?

我不怎麼想?

我做了什麼意味著看到米飯烹飪?我得到了這個夢想,五線的主要土壤,但是Tu Ke水是錢的跡象,職業生涯更加真誠。他真誠地誠實。這是豐富的,這是夏瑞的巨星
夢想你的意思是什麼?

夢想你的意思是什麼?

你是什​​麼意思夢想的夢想?夢想,主要木材的五個要素,水的跡象,主營業務,主營業務,案子應該抓住魅力,不能疏忽,春天夢想的吉利夢想夏天的夢想不幸。詢問學者夢想
拯救夢想

拯救夢想

拯救夢想什麼意思?你夢想著拯救人嗎?拯救人們的夢想有一個現實,也有夢想的主觀想像力,請參閱週宮官方網站拯救人民夢想的詳細解釋。夢想著敵人被拯救出來
2022愛方向和生日是在[質量個性]中

2022愛方向和生日是在[質量個性]中

[救生員]有人說,在出生88天之前,胎兒已經知道哪天的出生,如何有優質的個性,將走在什麼樣的愛情之旅,將與生活生活有什么生活。今天
夢想切割剪裁

夢想切割剪裁

夢想切割剪裁什麼意思?你夢想切你的手是好的嗎?夢想切割手工切割手有一個真正的影響和反應,也有夢想的主觀想像力。請參閱官方網站夢想的細節,以削減手
夢想著親人死了

夢想著親人死了

夢想著親人死了什麼意思?你夢想夢想你的親人死嗎?夢想有一個現實的影響和反應,還有夢想的主觀想像力,請參閱夢想世界夢想死亡的親屬的詳細解釋
夢想搶劫

夢想搶劫

夢想搶劫什麼意思?你夢想搶劫嗎?夢想著搶劫有一個現實的影響和反應,也有夢想的主觀想像力,請參閱週恭吉夢官方網站的詳細解釋。夢想搶劫
夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂什麼意思?你夢想缺乏異常藥物嗎?夢想缺乏現實世界的影響和現實,還有夢想的主觀想像,請看官方網站的夢想組織缺乏異常藥物。我覺得有些東西缺失了