JWT 和 JJWT,別再傻傻分不清了!
點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)
jwt是什么?
JWTs是JSON對(duì)象的編碼表示。JSON對(duì)象由零或多個(gè)名稱/值對(duì)組成,其中名稱為字符串,值為任意JSON值。
JWT有助于在clear(例如在URL中)發(fā)送這樣的信息,可以被信任為不可讀(即加密的)、不可修改的(即簽名)和URL - safe(即Base64編碼的)。
jwt的組成
Header: 標(biāo)題包含了令牌的元數(shù)據(jù),并且在最小包含簽名和/或加密算法的類型 Claims: Claims包含您想要簽署的任何信息 JSON Web Signature (JWS): 在header中指定的使用該算法的數(shù)字簽名和聲明
例如:
Header:
{
??"alg":?"HS256",
??"typ":?"JWT"
}
Claims:
{
??"sub":?"1234567890",
??"name":?"John?Doe",
??"admin":?true
}
Signature:
base64UrlEncode(Header)?+?"."?+?base64UrlEncode(Claims),
加密生成的token:

如何保證 JWT 安全
有很多庫(kù)可以幫助您創(chuàng)建和驗(yàn)證JWT,但是當(dāng)使用JWT時(shí),仍然可以做一些事情來(lái)限制您的安全風(fēng)險(xiǎn)。在您信任JWT中的任何信息之前,請(qǐng)始終驗(yàn)證簽名。這應(yīng)該是給定的。
換句話說(shuō),如果您正在傳遞一個(gè)秘密簽名密鑰到驗(yàn)證簽名的方法,并且簽名算法被設(shè)置為“none”,那么它應(yīng)該失敗驗(yàn)證。
確保簽名的秘密簽名,用于計(jì)算和驗(yàn)證簽名。秘密簽名密鑰只能由發(fā)行者和消費(fèi)者訪問(wèn),不能在這兩方之外訪問(wèn)。
不要在JWT中包含任何敏感數(shù)據(jù)。這些令牌通常是用來(lái)防止操作(未加密)的,因此索賠中的數(shù)據(jù)可以很容易地解碼和讀取。
如果您擔(dān)心重播攻擊,包括一個(gè)nonce(jti索賠)、過(guò)期時(shí)間(exp索賠)和創(chuàng)建時(shí)間(iat索賠)。這些在JWT規(guī)范中定義得很好。

jwt的框架:JJWT
JJWT的目標(biāo)是最容易使用和理解用于在JVM上創(chuàng)建和驗(yàn)證JSON Web令牌(JWTs)的庫(kù)。 JJWT是基于JWT、JWS、JWE、JWK和JWA RFC規(guī)范的Java實(shí)現(xiàn)。 JJWT還添加了一些不屬于規(guī)范的便利擴(kuò)展,比如JWT壓縮和索賠強(qiáng)制。
規(guī)范兼容:
創(chuàng)建和解析明文壓縮JWTs 創(chuàng)建、解析和驗(yàn)證所有標(biāo)準(zhǔn)JWS算法的數(shù)字簽名緊湊JWTs(又稱JWSs): HS256: HMAC using SHA-256 HS384: HMAC using SHA-384 HS512: HMAC using SHA-512 RS256: RSASSA-PKCS-v1_5 using SHA-256 RS384: RSASSA-PKCS-v1_5 using SHA-384 RS512: RSASSA-PKCS-v1_5 using SHA-512 PS256: RSASSA-PSS using SHA-256 and MGF1 with SHA-256 PS384: RSASSA-PSS using SHA-384 and MGF1 with SHA-384 PS512: RSASSA-PSS using SHA-512 and MGF1 with SHA-512 ES256: ECDSA using P-256 and SHA-256 ES384: ECDSA using P-384 and SHA-384 ES512: ECDSA using P-521 and SHA-512
這里以github上的demo演示,理解原理,集成到自己項(xiàng)目中即可。Spring Boot 基礎(chǔ)就不介紹了,推薦下這個(gè)實(shí)戰(zhàn)教程:https://www.javastack.cn/categories/Spring-Boot/
應(yīng)用采用 spring boot + angular + jwt
結(jié)構(gòu)圖

????4.0.0 ????com.nibado.example ????jwt-angular-spring ????0.0.2-SNAPSHOT ???????????? ????1.8 ????????1.8 ????????2.4 ????????0.6.0 ????????4.12 ????????1.5.3.RELEASE ???????????? ???????????????? ???????????????????? ????????org.springframework.boot ????????????????spring-boot-maven-plugin ????????????????${spring.boot.version} ???????????????????????????????????? ???????????????????????????????????? ???????????????????????????????????????????? ????????????????????repackage ???????????????????????????????? WebApplication.java" data-itemshowtype="0" tab="innerlink" data-linktype="2">???????????? ????????org.springframework.boot ????????????spring-boot-starter-web ????????????${spring.boot.version} ???????????????????? ????????commons-io ????????????commons-io ????????????${commons.io.version} ???????????????????? ????????io.jsonwebtoken ????????????jjwt ????????????${jjwt.version} ???????????????????? ????junit ????????????junit ????????????${junit.version} ????????"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">
????4.0.0
????com.nibado.example
????jwt-angular-spring
????0.0.2-SNAPSHOT
????
????????1.8
????????1.8
????????2.4
????????0.6.0
????????4.12
????????1.5.3.RELEASE
????
????
????????
????????????
????????????????org.springframework.boot
????????????????spring-boot-maven-plugin
????????????????${spring.boot.version}
????????????????
????????????????????
????????????????????????
????????????????????????????repackage
????????????????????????
????????????????????
????????????????
????????????
????????
????
????
????????
????????????org.springframework.boot
????????????spring-boot-starter-web
????????????${spring.boot.version}
????????
????????
????????????commons-io
????????????commons-io
????????????${commons.io.version}
????????
????????
????????????io.jsonwebtoken
????????????jjwt
????????????${jjwt.version}
????????
????????
????????????junit
????????????junit
????????????${junit.version}
????????
????
package?com.nibado.example.jwtangspr;
import?org.springframework.boot.SpringApplication;
import?org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import?org.springframework.boot.web.servlet.FilterRegistrationBean;
import?org.springframework.context.annotation.Bean;
import?org.springframework.context.annotation.ComponentScan;
import?org.springframework.context.annotation.Configuration;
@EnableAutoConfiguration
@ComponentScan
@Configuration
public?class?WebApplication?{
?
????//過(guò)濾器
????@Bean
????public?FilterRegistrationBean?jwtFilter()?{
????????final?FilterRegistrationBean?registrationBean?=?new?FilterRegistrationBean();
????????registrationBean.setFilter(new?JwtFilter());
????????registrationBean.addUrlPatterns("/api/*");
????????return?registrationBean;
????}
????public?static?void?main(final?String[]?args)?throws?Exception?{
????????SpringApplication.run(WebApplication.class,?args);
????}
}

>?userDb?=?new?HashMap<>();????@SuppressWarnings("unused")????private?static?class?UserLogin?{????????public?String?name;????????public?String?password;????}????????public?UserController()?{????????userDb.put("tom",?Arrays.asList("user"));????????userDb.put("wen",?Arrays.asList("user",?"admin"));????}????/*以上是模擬數(shù)據(jù)庫(kù),并往數(shù)據(jù)庫(kù)插入tom和sally兩條記錄*/????????????@RequestMapping(value?=?"login",?method?=?RequestMethod.POST)????public?LoginResponse?login(@RequestBody?final?UserLogin?login)????????throws?ServletException?{????????if?(login.name?==?null?||?!userDb.containsKey(login.name))?{????????????throw?new?ServletException("Invalid?login");????????}????????????????//加密生成token????????return?new?LoginResponse(Jwts.builder().setSubject(login.name)????????????.claim("roles",?userDb.get(login.name)).setIssuedAt(new?Date())????????????.signWith(SignatureAlgorithm.HS256,?"secretkey").compact());????}????@SuppressWarnings("unused")????private?static?class?LoginResponse?{????????public?String?token;????????public?LoginResponse(final?String?token)?{????????????this.token?=?token;????????}????}}ApiController.javapackage?com.nibado.example.jwtangspr;import?io.jsonwebtoken.Claims;import?java.util.List;import?javax.servlet.ServletException;import?javax.servlet.http.HttpServletRequest;import?org.springframework.web.bind.annotation.PathVariable;import?org.springframework.web.bind.annotation.RequestMapping;import?org.springframework.web.bind.annotation.RequestMethod;import?org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/api")public?class?ApiController?{?@SuppressWarnings("unchecked")?@RequestMapping(value?=?"role/{role}",?method?=?RequestMethod.GET)?public?Boolean?login(@PathVariable?final?String?role,???final?HttpServletRequest?request)?throws?ServletException?{??final?Claims?claims?=?(Claims)?request.getAttribute("claims");??return?((List{{greeting}}
?Success!?Welcome?{{userName}}
?????(logout)???
>?userDb?=?new?HashMap<>();????@SuppressWarnings("unused")????private?static?class?UserLogin?{????????public?String?name;????????public?String?password;????}????????public?UserController()?{????????userDb.put("tom",?Arrays.asList("user"));????????userDb.put("wen",?Arrays.asList("user",?"admin"));????}????/*以上是模擬數(shù)據(jù)庫(kù),并往數(shù)據(jù)庫(kù)插入tom和sally兩條記錄*/????????????@RequestMapping(value?=?"login",?method?=?RequestMethod.POST)????public?LoginResponse?login(@RequestBody?final?UserLogin?login)????????throws?ServletException?{????????if?(login.name?==?null?||?!userDb.containsKey(login.name))?{????????????throw?new?ServletException("Invalid?login");????????}????????????????//加密生成token????????return?new?LoginResponse(Jwts.builder().setSubject(login.name)????????????.claim("roles",?userDb.get(login.name)).setIssuedAt(new?Date())????????????.signWith(SignatureAlgorithm.HS256,?"secretkey").compact());????}????@SuppressWarnings("unused")????private?static?class?LoginResponse?{????????public?String?token;????????public?LoginResponse(final?String?token)?{????????????this.token?=?token;????????}????}}ApiController.javapackage?com.nibado.example.jwtangspr;import?io.jsonwebtoken.Claims;import?java.util.List;import?javax.servlet.ServletException;import?javax.servlet.http.HttpServletRequest;import?org.springframework.web.bind.annotation.PathVariable;import?org.springframework.web.bind.annotation.RequestMapping;import?org.springframework.web.bind.annotation.RequestMethod;import?org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/api")public?class?ApiController?{?@SuppressWarnings("unchecked")?@RequestMapping(value?=?"role/{role}",?method?=?RequestMethod.GET)?public?Boolean?login(@PathVariable?final?String?role,???final?HttpServletRequest?request)?throws?ServletException?{??final?Claims?claims?=?(Claims)?request.getAttribute("claims");??return?((List)?claims.get("roles")).contains(role);?}}index.html???????????? JSON?Web?Token?/?AngularJS?/?Spring?Boot?example ?????????????????????????????app.jsvar?appModule?=?angular.module('myApp',?[]);appModule.controller('MainCtrl',?['mainService','$scope','$http',????????function(mainService,?$scope,?$http)?{????????????$scope.greeting?=?'Welcome?to?the?JSON?Web?Token?/?AngularJR?/?Spring?example!';????????????$scope.token?=?null;????????????$scope.error?=?null;????????????$scope.roleUser?=?false;????????????$scope.roleAdmin?=?false;????????????$scope.roleFoo?=?false;????????????$scope.login?=?function()?{????????????????$scope.error?=?null;????????????????mainService.login($scope.userName).then(function(token)?{????????????????????$scope.token?=?token;????????????????????$http.defaults.headers.common.Authorization?=?'Bearer?'?+?token;????????????????????$scope.checkRoles();????????????????},????????????????function(error){????????????????????$scope.error?=?error????????????????????$scope.userName?=?'';????????????????});????????????}????????????$scope.checkRoles?=?function()?{????????????????mainService.hasRole('user').then(function(user)?{$scope.roleUser?=?user});????????????????mainService.hasRole('admin').then(function(admin)?{$scope.roleAdmin?=?admin});????????????????mainService.hasRole('foo').then(function(foo)?{$scope.roleFoo?=?foo});????????????}????????????$scope.logout?=?function()?{????????????????$scope.userName?=?'';????????????????$scope.token?=?null;????????????????$http.defaults.headers.common.Authorization?=?'';????????????}????????????$scope.loggedIn?=?function()?{????????????????return?$scope.token?!==?null;????????????}????????}?]);appModule.service('mainService',?function($http)?{????return?{????????login?:?function(username)?{????????????return?$http.post('/user/login',?{name:?username}).then(function(response)?{????????????????return?response.data.token;????????????});????????},????????hasRole?:?function(role)?{????????????return?$http.get('/api/role/'?+?role).then(function(response){????????????????console.log(response);????????????????return?response.data;????????????});????????}????};});運(yùn)行應(yīng)用" data-itemshowtype="0" tab="innerlink" data-linktype="2">{{greeting}}
???Please?log?in?(tom?and?sally?are?valid?names)??????????Error:???{{error.data.message}}????????????????Success!?Welcome?{{userName}}
?????(logout)?????????????{{userName}}?is?a???????User???????????Admin???????????????Foo?????package?com.nibado.example.jwtangspr;
import?java.io.IOException;
import?javax.servlet.FilterChain;
import?javax.servlet.ServletException;
import?javax.servlet.ServletRequest;
import?javax.servlet.ServletResponse;
import?javax.servlet.http.HttpServletRequest;
import?org.springframework.web.filter.GenericFilterBean;
import?io.jsonwebtoken.Claims;
import?io.jsonwebtoken.Jwts;
import?io.jsonwebtoken.SignatureException;
public?class?JwtFilter?extends?GenericFilterBean?{
????@Override
????public?void?doFilter(final?ServletRequest?req,
?????????????????????????final?ServletResponse?res,
?????????????????????????final?FilterChain?chain)?throws?IOException,?ServletException?{
????????final?HttpServletRequest?request?=?(HttpServletRequest)?req;
????????//客戶端將token封裝在請(qǐng)求頭中,格式為(Bearer后加空格):Authorization:Bearer +token
????????final?String?authHeader?=?request.getHeader("Authorization");
????????if?(authHeader?==?null?||?!authHeader.startsWith("Bearer?"))?{
????????????throw?new?ServletException("Missing?or?invalid?Authorization?header.");
????????}
????????//去除Bearer?后部分
????????final?String?token?=?authHeader.substring(7);
????????try?{
?????????//解密token,拿到里面的對(duì)象claims
????????????final?Claims?claims?=?Jwts.parser().setSigningKey("secretkey")
????????????????.parseClaimsJws(token).getBody();
????????????//將對(duì)象傳遞給下一個(gè)請(qǐng)求
????????????request.setAttribute("claims",?claims);
????????}
????????catch?(final?SignatureException?e)?{
????????????throw?new?ServletException("Invalid?token.");
????????}
????????chain.doFilter(req,?res);
????}
}
>?userDb?=?new?HashMap<>();????@SuppressWarnings("unused")????private?static?class?UserLogin?{????????public?String?name;????????public?String?password;????}????????public?UserController()?{????????userDb.put("tom",?Arrays.asList("user"));????????userDb.put("wen",?Arrays.asList("user",?"admin"));????}????/*以上是模擬數(shù)據(jù)庫(kù),并往數(shù)據(jù)庫(kù)插入tom和sally兩條記錄*/????????????@RequestMapping(value?=?"login",?method?=?RequestMethod.POST)????public?LoginResponse?login(@RequestBody?final?UserLogin?login)????????throws?ServletException?{????????if?(login.name?==?null?||?!userDb.containsKey(login.name))?{????????????throw?new?ServletException("Invalid?login");????????}????????????????//加密生成token????????return?new?LoginResponse(Jwts.builder().setSubject(login.name)????????????.claim("roles",?userDb.get(login.name)).setIssuedAt(new?Date())????????????.signWith(SignatureAlgorithm.HS256,?"secretkey").compact());????}????@SuppressWarnings("unused")????private?static?class?LoginResponse?{????????public?String?token;????????public?LoginResponse(final?String?token)?{????????????this.token?=?token;????????}????}}ApiController.javapackage?com.nibado.example.jwtangspr;import?io.jsonwebtoken.Claims;import?java.util.List;import?javax.servlet.ServletException;import?javax.servlet.http.HttpServletRequest;import?org.springframework.web.bind.annotation.PathVariable;import?org.springframework.web.bind.annotation.RequestMapping;import?org.springframework.web.bind.annotation.RequestMethod;import?org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/api")public?class?ApiController?{?@SuppressWarnings("unchecked")?@RequestMapping(value?=?"role/{role}",?method?=?RequestMethod.GET)?public?Boolean?login(@PathVariable?final?String?role,???final?HttpServletRequest?request)?throws?ServletException?{??final?Claims?claims?=?(Claims)?request.getAttribute("claims");??return?((List{{greeting}}
?Success!?Welcome?{{userName}}
?????(logout)???
>?userDb?=?new?HashMap<>();????@SuppressWarnings("unused")????private?static?class?UserLogin?{????????public?String?name;????????public?String?password;????}????????public?UserController()?{????????userDb.put("tom",?Arrays.asList("user"));????????userDb.put("wen",?Arrays.asList("user",?"admin"));????}????/*以上是模擬數(shù)據(jù)庫(kù),并往數(shù)據(jù)庫(kù)插入tom和sally兩條記錄*/????????????@RequestMapping(value?=?"login",?method?=?RequestMethod.POST)????public?LoginResponse?login(@RequestBody?final?UserLogin?login)????????throws?ServletException?{????????if?(login.name?==?null?||?!userDb.containsKey(login.name))?{????????????throw?new?ServletException("Invalid?login");????????}????????????????//加密生成token????????return?new?LoginResponse(Jwts.builder().setSubject(login.name)????????????.claim("roles",?userDb.get(login.name)).setIssuedAt(new?Date())????????????.signWith(SignatureAlgorithm.HS256,?"secretkey").compact());????}????@SuppressWarnings("unused")????private?static?class?LoginResponse?{????????public?String?token;????????public?LoginResponse(final?String?token)?{????????????this.token?=?token;????????}????}}ApiController.javapackage?com.nibado.example.jwtangspr;import?io.jsonwebtoken.Claims;import?java.util.List;import?javax.servlet.ServletException;import?javax.servlet.http.HttpServletRequest;import?org.springframework.web.bind.annotation.PathVariable;import?org.springframework.web.bind.annotation.RequestMapping;import?org.springframework.web.bind.annotation.RequestMethod;import?org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/api")public?class?ApiController?{?@SuppressWarnings("unchecked")?@RequestMapping(value?=?"role/{role}",?method?=?RequestMethod.GET)?public?Boolean?login(@PathVariable?final?String?role,???final?HttpServletRequest?request)?throws?ServletException?{??final?Claims?claims?=?(Claims)?request.getAttribute("claims");??return?((List)?claims.get("roles")).contains(role);?}}index.html???????????? JSON?Web?Token?/?AngularJS?/?Spring?Boot?example ?????????????????????????????app.jsvar?appModule?=?angular.module('myApp',?[]);appModule.controller('MainCtrl',?['mainService','$scope','$http',????????function(mainService,?$scope,?$http)?{????????????$scope.greeting?=?'Welcome?to?the?JSON?Web?Token?/?AngularJR?/?Spring?example!';????????????$scope.token?=?null;????????????$scope.error?=?null;????????????$scope.roleUser?=?false;????????????$scope.roleAdmin?=?false;????????????$scope.roleFoo?=?false;????????????$scope.login?=?function()?{????????????????$scope.error?=?null;????????????????mainService.login($scope.userName).then(function(token)?{????????????????????$scope.token?=?token;????????????????????$http.defaults.headers.common.Authorization?=?'Bearer?'?+?token;????????????????????$scope.checkRoles();????????????????},????????????????function(error){????????????????????$scope.error?=?error????????????????????$scope.userName?=?'';????????????????});????????????}????????????$scope.checkRoles?=?function()?{????????????????mainService.hasRole('user').then(function(user)?{$scope.roleUser?=?user});????????????????mainService.hasRole('admin').then(function(admin)?{$scope.roleAdmin?=?admin});????????????????mainService.hasRole('foo').then(function(foo)?{$scope.roleFoo?=?foo});????????????}????????????$scope.logout?=?function()?{????????????????$scope.userName?=?'';????????????????$scope.token?=?null;????????????????$http.defaults.headers.common.Authorization?=?'';????????????}????????????$scope.loggedIn?=?function()?{????????????????return?$scope.token?!==?null;????????????}????????}?]);appModule.service('mainService',?function($http)?{????return?{????????login?:?function(username)?{????????????return?$http.post('/user/login',?{name:?username}).then(function(response)?{????????????????return?response.data.token;????????????});????????},????????hasRole?:?function(role)?{????????????return?$http.get('/api/role/'?+?role).then(function(response){????????????????console.log(response);????????????????return?response.data;????????????});????????}????};});運(yùn)行應(yīng)用" data-itemshowtype="0" tab="innerlink" data-linktype="2">{{greeting}}
???Please?log?in?(tom?and?sally?are?valid?names)??????????Error:???{{error.data.message}}????????????????Success!?Welcome?{{userName}}
?????(logout)?????????????{{userName}}?is?a???????User???????????Admin???????????????Foo?????package?com.nibado.example.jwtangspr;
import?java.util.Arrays;
import?java.util.Date;
import?java.util.HashMap;
import?java.util.List;
import?java.util.Map;
import?javax.servlet.ServletException;
import?org.springframework.web.bind.annotation.RequestBody;
import?org.springframework.web.bind.annotation.RequestMapping;
import?org.springframework.web.bind.annotation.RequestMethod;
import?org.springframework.web.bind.annotation.RestController;
import?io.jsonwebtoken.Jwts;
import?io.jsonwebtoken.SignatureAlgorithm;
@RestController
@RequestMapping("/user")
public?class?UserController?{
?//這里模擬數(shù)據(jù)庫(kù)
????private?final?Map>?userDb?=?new?HashMap<>();
????@SuppressWarnings("unused")
????private?static?class?UserLogin?{
????????public?String?name;
????????public?String?password;
????}
????
????public?UserController()?{
????????userDb.put("tom",?Arrays.asList("user"));
????????userDb.put("wen",?Arrays.asList("user",?"admin"));
????}
????/*以上是模擬數(shù)據(jù)庫(kù),并往數(shù)據(jù)庫(kù)插入tom和sally兩條記錄*/
????
????
????@RequestMapping(value?=?"login",?method?=?RequestMethod.POST)
????public?LoginResponse?login(@RequestBody?final?UserLogin?login)
????????throws?ServletException?{
????????if?(login.name?==?null?||?!userDb.containsKey(login.name))?{
????????????throw?new?ServletException("Invalid?login");
????????}
????????
????????//加密生成token
????????return?new?LoginResponse(Jwts.builder().setSubject(login.name)
????????????.claim("roles",?userDb.get(login.name)).setIssuedAt(new?Date())
????????????.signWith(SignatureAlgorithm.HS256,?"secretkey").compact());
????}
????@SuppressWarnings("unused")
????private?static?class?LoginResponse?{
????????public?String?token;
????????public?LoginResponse(final?String?token)?{
????????????this.token?=?token;
????????}
????}
}
>?userDb?=?new?HashMap<>();????@SuppressWarnings("unused")????private?static?class?UserLogin?{????????public?String?name;????????public?String?password;????}????????public?UserController()?{????????userDb.put("tom",?Arrays.asList("user"));????????userDb.put("wen",?Arrays.asList("user",?"admin"));????}????/*以上是模擬數(shù)據(jù)庫(kù),并往數(shù)據(jù)庫(kù)插入tom和sally兩條記錄*/????????????@RequestMapping(value?=?"login",?method?=?RequestMethod.POST)????public?LoginResponse?login(@RequestBody?final?UserLogin?login)????????throws?ServletException?{????????if?(login.name?==?null?||?!userDb.containsKey(login.name))?{????????????throw?new?ServletException("Invalid?login");????????}????????????????//加密生成token????????return?new?LoginResponse(Jwts.builder().setSubject(login.name)????????????.claim("roles",?userDb.get(login.name)).setIssuedAt(new?Date())????????????.signWith(SignatureAlgorithm.HS256,?"secretkey").compact());????}????@SuppressWarnings("unused")????private?static?class?LoginResponse?{????????public?String?token;????????public?LoginResponse(final?String?token)?{????????????this.token?=?token;????????}????}}ApiController.javapackage?com.nibado.example.jwtangspr;import?io.jsonwebtoken.Claims;import?java.util.List;import?javax.servlet.ServletException;import?javax.servlet.http.HttpServletRequest;import?org.springframework.web.bind.annotation.PathVariable;import?org.springframework.web.bind.annotation.RequestMapping;import?org.springframework.web.bind.annotation.RequestMethod;import?org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/api")public?class?ApiController?{?@SuppressWarnings("unchecked")?@RequestMapping(value?=?"role/{role}",?method?=?RequestMethod.GET)?public?Boolean?login(@PathVariable?final?String?role,???final?HttpServletRequest?request)?throws?ServletException?{??final?Claims?claims?=?(Claims)?request.getAttribute("claims");??return?((List{{greeting}}
?Success!?Welcome?{{userName}}
?????(logout)???
>?userDb?=?new?HashMap<>();????@SuppressWarnings("unused")????private?static?class?UserLogin?{????????public?String?name;????????public?String?password;????}????????public?UserController()?{????????userDb.put("tom",?Arrays.asList("user"));????????userDb.put("wen",?Arrays.asList("user",?"admin"));????}????/*以上是模擬數(shù)據(jù)庫(kù),并往數(shù)據(jù)庫(kù)插入tom和sally兩條記錄*/????????????@RequestMapping(value?=?"login",?method?=?RequestMethod.POST)????public?LoginResponse?login(@RequestBody?final?UserLogin?login)????????throws?ServletException?{????????if?(login.name?==?null?||?!userDb.containsKey(login.name))?{????????????throw?new?ServletException("Invalid?login");????????}????????????????//加密生成token????????return?new?LoginResponse(Jwts.builder().setSubject(login.name)????????????.claim("roles",?userDb.get(login.name)).setIssuedAt(new?Date())????????????.signWith(SignatureAlgorithm.HS256,?"secretkey").compact());????}????@SuppressWarnings("unused")????private?static?class?LoginResponse?{????????public?String?token;????????public?LoginResponse(final?String?token)?{????????????this.token?=?token;????????}????}}ApiController.javapackage?com.nibado.example.jwtangspr;import?io.jsonwebtoken.Claims;import?java.util.List;import?javax.servlet.ServletException;import?javax.servlet.http.HttpServletRequest;import?org.springframework.web.bind.annotation.PathVariable;import?org.springframework.web.bind.annotation.RequestMapping;import?org.springframework.web.bind.annotation.RequestMethod;import?org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/api")public?class?ApiController?{?@SuppressWarnings("unchecked")?@RequestMapping(value?=?"role/{role}",?method?=?RequestMethod.GET)?public?Boolean?login(@PathVariable?final?String?role,???final?HttpServletRequest?request)?throws?ServletException?{??final?Claims?claims?=?(Claims)?request.getAttribute("claims");??return?((List)?claims.get("roles")).contains(role);?}}index.html???????????? JSON?Web?Token?/?AngularJS?/?Spring?Boot?example ?????????????????????????????app.jsvar?appModule?=?angular.module('myApp',?[]);appModule.controller('MainCtrl',?['mainService','$scope','$http',????????function(mainService,?$scope,?$http)?{????????????$scope.greeting?=?'Welcome?to?the?JSON?Web?Token?/?AngularJR?/?Spring?example!';????????????$scope.token?=?null;????????????$scope.error?=?null;????????????$scope.roleUser?=?false;????????????$scope.roleAdmin?=?false;????????????$scope.roleFoo?=?false;????????????$scope.login?=?function()?{????????????????$scope.error?=?null;????????????????mainService.login($scope.userName).then(function(token)?{????????????????????$scope.token?=?token;????????????????????$http.defaults.headers.common.Authorization?=?'Bearer?'?+?token;????????????????????$scope.checkRoles();????????????????},????????????????function(error){????????????????????$scope.error?=?error????????????????????$scope.userName?=?'';????????????????});????????????}????????????$scope.checkRoles?=?function()?{????????????????mainService.hasRole('user').then(function(user)?{$scope.roleUser?=?user});????????????????mainService.hasRole('admin').then(function(admin)?{$scope.roleAdmin?=?admin});????????????????mainService.hasRole('foo').then(function(foo)?{$scope.roleFoo?=?foo});????????????}????????????$scope.logout?=?function()?{????????????????$scope.userName?=?'';????????????????$scope.token?=?null;????????????????$http.defaults.headers.common.Authorization?=?'';????????????}????????????$scope.loggedIn?=?function()?{????????????????return?$scope.token?!==?null;????????????}????????}?]);appModule.service('mainService',?function($http)?{????return?{????????login?:?function(username)?{????????????return?$http.post('/user/login',?{name:?username}).then(function(response)?{????????????????return?response.data.token;????????????});????????},????????hasRole?:?function(role)?{????????????return?$http.get('/api/role/'?+?role).then(function(response){????????????????console.log(response);????????????????return?response.data;????????????});????????}????};});運(yùn)行應(yīng)用" data-itemshowtype="0" tab="innerlink" data-linktype="2">{{greeting}}
???Please?log?in?(tom?and?sally?are?valid?names)??????????Error:???{{error.data.message}}????????????????Success!?Welcome?{{userName}}
?????(logout)?????????????{{userName}}?is?a???????User???????????Admin???????????????Foo?????package?com.nibado.example.jwtangspr;
import?io.jsonwebtoken.Claims;
import?java.util.List;
import?javax.servlet.ServletException;
import?javax.servlet.http.HttpServletRequest;
import?org.springframework.web.bind.annotation.PathVariable;
import?org.springframework.web.bind.annotation.RequestMapping;
import?org.springframework.web.bind.annotation.RequestMethod;
import?org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public?class?ApiController?{
?@SuppressWarnings("unchecked")
?@RequestMapping(value?=?"role/{role}",?method?=?RequestMethod.GET)
?public?Boolean?login(@PathVariable?final?String?role,
???final?HttpServletRequest?request)?throws?ServletException?{
??final?Claims?claims?=?(Claims)?request.getAttribute("claims");
??return?((List)?claims.get("roles")).contains(role);
?}
}
>?userDb?=?new?HashMap<>();????@SuppressWarnings("unused")????private?static?class?UserLogin?{????????public?String?name;????????public?String?password;????}????????public?UserController()?{????????userDb.put("tom",?Arrays.asList("user"));????????userDb.put("wen",?Arrays.asList("user",?"admin"));????}????/*以上是模擬數(shù)據(jù)庫(kù),并往數(shù)據(jù)庫(kù)插入tom和sally兩條記錄*/????????????@RequestMapping(value?=?"login",?method?=?RequestMethod.POST)????public?LoginResponse?login(@RequestBody?final?UserLogin?login)????????throws?ServletException?{????????if?(login.name?==?null?||?!userDb.containsKey(login.name))?{????????????throw?new?ServletException("Invalid?login");????????}????????????????//加密生成token????????return?new?LoginResponse(Jwts.builder().setSubject(login.name)????????????.claim("roles",?userDb.get(login.name)).setIssuedAt(new?Date())????????????.signWith(SignatureAlgorithm.HS256,?"secretkey").compact());????}????@SuppressWarnings("unused")????private?static?class?LoginResponse?{????????public?String?token;????????public?LoginResponse(final?String?token)?{????????????this.token?=?token;????????}????}}ApiController.javapackage?com.nibado.example.jwtangspr;import?io.jsonwebtoken.Claims;import?java.util.List;import?javax.servlet.ServletException;import?javax.servlet.http.HttpServletRequest;import?org.springframework.web.bind.annotation.PathVariable;import?org.springframework.web.bind.annotation.RequestMapping;import?org.springframework.web.bind.annotation.RequestMethod;import?org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/api")public?class?ApiController?{?@SuppressWarnings("unchecked")?@RequestMapping(value?=?"role/{role}",?method?=?RequestMethod.GET)?public?Boolean?login(@PathVariable?final?String?role,???final?HttpServletRequest?request)?throws?ServletException?{??final?Claims?claims?=?(Claims)?request.getAttribute("claims");??return?((List{{greeting}}
?Success!?Welcome?{{userName}}
?????(logout)???
>?userDb?=?new?HashMap<>();????@SuppressWarnings("unused")????private?static?class?UserLogin?{????????public?String?name;????????public?String?password;????}????????public?UserController()?{????????userDb.put("tom",?Arrays.asList("user"));????????userDb.put("wen",?Arrays.asList("user",?"admin"));????}????/*以上是模擬數(shù)據(jù)庫(kù),并往數(shù)據(jù)庫(kù)插入tom和sally兩條記錄*/????????????@RequestMapping(value?=?"login",?method?=?RequestMethod.POST)????public?LoginResponse?login(@RequestBody?final?UserLogin?login)????????throws?ServletException?{????????if?(login.name?==?null?||?!userDb.containsKey(login.name))?{????????????throw?new?ServletException("Invalid?login");????????}????????????????//加密生成token????????return?new?LoginResponse(Jwts.builder().setSubject(login.name)????????????.claim("roles",?userDb.get(login.name)).setIssuedAt(new?Date())????????????.signWith(SignatureAlgorithm.HS256,?"secretkey").compact());????}????@SuppressWarnings("unused")????private?static?class?LoginResponse?{????????public?String?token;????????public?LoginResponse(final?String?token)?{????????????this.token?=?token;????????}????}}ApiController.javapackage?com.nibado.example.jwtangspr;import?io.jsonwebtoken.Claims;import?java.util.List;import?javax.servlet.ServletException;import?javax.servlet.http.HttpServletRequest;import?org.springframework.web.bind.annotation.PathVariable;import?org.springframework.web.bind.annotation.RequestMapping;import?org.springframework.web.bind.annotation.RequestMethod;import?org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/api")public?class?ApiController?{?@SuppressWarnings("unchecked")?@RequestMapping(value?=?"role/{role}",?method?=?RequestMethod.GET)?public?Boolean?login(@PathVariable?final?String?role,???final?HttpServletRequest?request)?throws?ServletException?{??final?Claims?claims?=?(Claims)?request.getAttribute("claims");??return?((List)?claims.get("roles")).contains(role);?}}index.html???????????? JSON?Web?Token?/?AngularJS?/?Spring?Boot?example ?????????????????????????????app.jsvar?appModule?=?angular.module('myApp',?[]);appModule.controller('MainCtrl',?['mainService','$scope','$http',????????function(mainService,?$scope,?$http)?{????????????$scope.greeting?=?'Welcome?to?the?JSON?Web?Token?/?AngularJR?/?Spring?example!';????????????$scope.token?=?null;????????????$scope.error?=?null;????????????$scope.roleUser?=?false;????????????$scope.roleAdmin?=?false;????????????$scope.roleFoo?=?false;????????????$scope.login?=?function()?{????????????????$scope.error?=?null;????????????????mainService.login($scope.userName).then(function(token)?{????????????????????$scope.token?=?token;????????????????????$http.defaults.headers.common.Authorization?=?'Bearer?'?+?token;????????????????????$scope.checkRoles();????????????????},????????????????function(error){????????????????????$scope.error?=?error????????????????????$scope.userName?=?'';????????????????});????????????}????????????$scope.checkRoles?=?function()?{????????????????mainService.hasRole('user').then(function(user)?{$scope.roleUser?=?user});????????????????mainService.hasRole('admin').then(function(admin)?{$scope.roleAdmin?=?admin});????????????????mainService.hasRole('foo').then(function(foo)?{$scope.roleFoo?=?foo});????????????}????????????$scope.logout?=?function()?{????????????????$scope.userName?=?'';????????????????$scope.token?=?null;????????????????$http.defaults.headers.common.Authorization?=?'';????????????}????????????$scope.loggedIn?=?function()?{????????????????return?$scope.token?!==?null;????????????}????????}?]);appModule.service('mainService',?function($http)?{????return?{????????login?:?function(username)?{????????????return?$http.post('/user/login',?{name:?username}).then(function(response)?{????????????????return?response.data.token;????????????});????????},????????hasRole?:?function(role)?{????????????return?$http.get('/api/role/'?+?role).then(function(response){????????????????console.log(response);????????????????return?response.data;????????????});????????}????};});運(yùn)行應(yīng)用" data-itemshowtype="0" tab="innerlink" data-linktype="2">{{greeting}}
???Please?log?in?(tom?and?sally?are?valid?names)??????????Error:???{{error.data.message}}????????????????Success!?Welcome?{{userName}}
?????(logout)?????????????{{userName}}?is?a???????User???????????Admin???????????????Foo?????
"myApp">
????"utf-8"/>
????"X-UA-Compatible"?content="IE=edge,chrome=1"/>
????JSON?Web?Token?/?AngularJS?/?Spring?Boot?example
????"description"?content="">
????"viewport"?content="width=device-width">
????"stylesheet"?href="libs/bootstrap/css/bootstrap.css">
????
????
????
????"container"?ng-controller='MainCtrl'>
?{{greeting}}
?"!loggedIn()">
??Please?log?in?(tom?and?sally?are?valid?names)
??
?
?"alert?alert-danger"?role="alert"?ng-show="error.data.message">?
???"glyphicon?glyphicon-exclamation-sign"?aria-hidden="true">
???"sr-only">Error:
???{{error.data.message}}
?
?"loggedIn()">
??"row">
???
???"col-md-4">
???"row?header">
???"col-sm-4">{{userName}}?is?a
??
??"row">
???"col-sm-2">User
???"col-sm-2">"glyphicon?glyphicon-ok"?aria-hidden="true"?ng-show="roleUser">
??
??"row">?
???"col-sm-2">Admin
???"col-sm-2">"glyphicon?glyphicon-ok"?aria-hidden="true"?ng-show="roleAdmin">
??
??"row">????
???"col-sm-2">Foo
???"col-sm-2">"glyphicon?glyphicon-ok"?aria-hidden="true"?ng-show="roleFoo">
??
???
??
?
>?userDb?=?new?HashMap<>();????@SuppressWarnings("unused")????private?static?class?UserLogin?{????????public?String?name;????????public?String?password;????}????????public?UserController()?{????????userDb.put("tom",?Arrays.asList("user"));????????userDb.put("wen",?Arrays.asList("user",?"admin"));????}????/*以上是模擬數(shù)據(jù)庫(kù),并往數(shù)據(jù)庫(kù)插入tom和sally兩條記錄*/????????????@RequestMapping(value?=?"login",?method?=?RequestMethod.POST)????public?LoginResponse?login(@RequestBody?final?UserLogin?login)????????throws?ServletException?{????????if?(login.name?==?null?||?!userDb.containsKey(login.name))?{????????????throw?new?ServletException("Invalid?login");????????}????????????????//加密生成token????????return?new?LoginResponse(Jwts.builder().setSubject(login.name)????????????.claim("roles",?userDb.get(login.name)).setIssuedAt(new?Date())????????????.signWith(SignatureAlgorithm.HS256,?"secretkey").compact());????}????@SuppressWarnings("unused")????private?static?class?LoginResponse?{????????public?String?token;????????public?LoginResponse(final?String?token)?{????????????this.token?=?token;????????}????}}ApiController.javapackage?com.nibado.example.jwtangspr;import?io.jsonwebtoken.Claims;import?java.util.List;import?javax.servlet.ServletException;import?javax.servlet.http.HttpServletRequest;import?org.springframework.web.bind.annotation.PathVariable;import?org.springframework.web.bind.annotation.RequestMapping;import?org.springframework.web.bind.annotation.RequestMethod;import?org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/api")public?class?ApiController?{?@SuppressWarnings("unchecked")?@RequestMapping(value?=?"role/{role}",?method?=?RequestMethod.GET)?public?Boolean?login(@PathVariable?final?String?role,???final?HttpServletRequest?request)?throws?ServletException?{??final?Claims?claims?=?(Claims)?request.getAttribute("claims");??return?((List{{greeting}}
?Success!?Welcome?{{userName}}
?????(logout)???
>?userDb?=?new?HashMap<>();????@SuppressWarnings("unused")????private?static?class?UserLogin?{????????public?String?name;????????public?String?password;????}????????public?UserController()?{????????userDb.put("tom",?Arrays.asList("user"));????????userDb.put("wen",?Arrays.asList("user",?"admin"));????}????/*以上是模擬數(shù)據(jù)庫(kù),并往數(shù)據(jù)庫(kù)插入tom和sally兩條記錄*/????????????@RequestMapping(value?=?"login",?method?=?RequestMethod.POST)????public?LoginResponse?login(@RequestBody?final?UserLogin?login)????????throws?ServletException?{????????if?(login.name?==?null?||?!userDb.containsKey(login.name))?{????????????throw?new?ServletException("Invalid?login");????????}????????????????//加密生成token????????return?new?LoginResponse(Jwts.builder().setSubject(login.name)????????????.claim("roles",?userDb.get(login.name)).setIssuedAt(new?Date())????????????.signWith(SignatureAlgorithm.HS256,?"secretkey").compact());????}????@SuppressWarnings("unused")????private?static?class?LoginResponse?{????????public?String?token;????????public?LoginResponse(final?String?token)?{????????????this.token?=?token;????????}????}}ApiController.javapackage?com.nibado.example.jwtangspr;import?io.jsonwebtoken.Claims;import?java.util.List;import?javax.servlet.ServletException;import?javax.servlet.http.HttpServletRequest;import?org.springframework.web.bind.annotation.PathVariable;import?org.springframework.web.bind.annotation.RequestMapping;import?org.springframework.web.bind.annotation.RequestMethod;import?org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/api")public?class?ApiController?{?@SuppressWarnings("unchecked")?@RequestMapping(value?=?"role/{role}",?method?=?RequestMethod.GET)?public?Boolean?login(@PathVariable?final?String?role,???final?HttpServletRequest?request)?throws?ServletException?{??final?Claims?claims?=?(Claims)?request.getAttribute("claims");??return?((List)?claims.get("roles")).contains(role);?}}index.html???????????? JSON?Web?Token?/?AngularJS?/?Spring?Boot?example ?????????????????????????????app.jsvar?appModule?=?angular.module('myApp',?[]);appModule.controller('MainCtrl',?['mainService','$scope','$http',????????function(mainService,?$scope,?$http)?{????????????$scope.greeting?=?'Welcome?to?the?JSON?Web?Token?/?AngularJR?/?Spring?example!';????????????$scope.token?=?null;????????????$scope.error?=?null;????????????$scope.roleUser?=?false;????????????$scope.roleAdmin?=?false;????????????$scope.roleFoo?=?false;????????????$scope.login?=?function()?{????????????????$scope.error?=?null;????????????????mainService.login($scope.userName).then(function(token)?{????????????????????$scope.token?=?token;????????????????????$http.defaults.headers.common.Authorization?=?'Bearer?'?+?token;????????????????????$scope.checkRoles();????????????????},????????????????function(error){????????????????????$scope.error?=?error????????????????????$scope.userName?=?'';????????????????});????????????}????????????$scope.checkRoles?=?function()?{????????????????mainService.hasRole('user').then(function(user)?{$scope.roleUser?=?user});????????????????mainService.hasRole('admin').then(function(admin)?{$scope.roleAdmin?=?admin});????????????????mainService.hasRole('foo').then(function(foo)?{$scope.roleFoo?=?foo});????????????}????????????$scope.logout?=?function()?{????????????????$scope.userName?=?'';????????????????$scope.token?=?null;????????????????$http.defaults.headers.common.Authorization?=?'';????????????}????????????$scope.loggedIn?=?function()?{????????????????return?$scope.token?!==?null;????????????}????????}?]);appModule.service('mainService',?function($http)?{????return?{????????login?:?function(username)?{????????????return?$http.post('/user/login',?{name:?username}).then(function(response)?{????????????????return?response.data.token;????????????});????????},????????hasRole?:?function(role)?{????????????return?$http.get('/api/role/'?+?role).then(function(response){????????????????console.log(response);????????????????return?response.data;????????????});????????}????};});運(yùn)行應(yīng)用" data-itemshowtype="0" tab="innerlink" data-linktype="2">{{greeting}}
???Please?log?in?(tom?and?sally?are?valid?names)??????????Error:???{{error.data.message}}????????????????Success!?Welcome?{{userName}}
?????(logout)?????????????{{userName}}?is?a???????User???????????Admin???????????????Foo?????var?appModule?=?angular.module('myApp',?[]);
appModule.controller('MainCtrl',?['mainService','$scope','$http',
????????function(mainService,?$scope,?$http)?{
????????????$scope.greeting?=?'Welcome?to?the?JSON?Web?Token?/?AngularJR?/?Spring?example!';
????????????$scope.token?=?null;
????????????$scope.error?=?null;
????????????$scope.roleUser?=?false;
????????????$scope.roleAdmin?=?false;
????????????$scope.roleFoo?=?false;
????????????$scope.login?=?function()?{
????????????????$scope.error?=?null;
????????????????mainService.login($scope.userName).then(function(token)?{
????????????????????$scope.token?=?token;
????????????????????$http.defaults.headers.common.Authorization?=?'Bearer?'?+?token;
????????????????????$scope.checkRoles();
????????????????},
????????????????function(error){
????????????????????$scope.error?=?error
????????????????????$scope.userName?=?'';
????????????????});
????????????}
????????????$scope.checkRoles?=?function()?{
????????????????mainService.hasRole('user').then(function(user)?{$scope.roleUser?=?user});
????????????????mainService.hasRole('admin').then(function(admin)?{$scope.roleAdmin?=?admin});
????????????????mainService.hasRole('foo').then(function(foo)?{$scope.roleFoo?=?foo});
????????????}
????????????$scope.logout?=?function()?{
????????????????$scope.userName?=?'';
????????????????$scope.token?=?null;
????????????????$http.defaults.headers.common.Authorization?=?'';
????????????}
????????????$scope.loggedIn?=?function()?{
????????????????return?$scope.token?!==?null;
????????????}
????????}?]);
appModule.service('mainService',?function($http)?{
????return?{
????????login?:?function(username)?{
????????????return?$http.post('/user/login',?{name:?username}).then(function(response)?{
????????????????return?response.data.token;
????????????});
????????},
????????hasRole?:?function(role)?{
????????????return?$http.get('/api/role/'?+?role).then(function(response){
????????????????console.log(response);
????????????????return?response.data;
????????????});
????????}
????};
});
>?userDb?=?new?HashMap<>();????@SuppressWarnings("unused")????private?static?class?UserLogin?{????????public?String?name;????????public?String?password;????}????????public?UserController()?{????????userDb.put("tom",?Arrays.asList("user"));????????userDb.put("wen",?Arrays.asList("user",?"admin"));????}????/*以上是模擬數(shù)據(jù)庫(kù),并往數(shù)據(jù)庫(kù)插入tom和sally兩條記錄*/????????????@RequestMapping(value?=?"login",?method?=?RequestMethod.POST)????public?LoginResponse?login(@RequestBody?final?UserLogin?login)????????throws?ServletException?{????????if?(login.name?==?null?||?!userDb.containsKey(login.name))?{????????????throw?new?ServletException("Invalid?login");????????}????????????????//加密生成token????????return?new?LoginResponse(Jwts.builder().setSubject(login.name)????????????.claim("roles",?userDb.get(login.name)).setIssuedAt(new?Date())????????????.signWith(SignatureAlgorithm.HS256,?"secretkey").compact());????}????@SuppressWarnings("unused")????private?static?class?LoginResponse?{????????public?String?token;????????public?LoginResponse(final?String?token)?{????????????this.token?=?token;????????}????}}ApiController.javapackage?com.nibado.example.jwtangspr;import?io.jsonwebtoken.Claims;import?java.util.List;import?javax.servlet.ServletException;import?javax.servlet.http.HttpServletRequest;import?org.springframework.web.bind.annotation.PathVariable;import?org.springframework.web.bind.annotation.RequestMapping;import?org.springframework.web.bind.annotation.RequestMethod;import?org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/api")public?class?ApiController?{?@SuppressWarnings("unchecked")?@RequestMapping(value?=?"role/{role}",?method?=?RequestMethod.GET)?public?Boolean?login(@PathVariable?final?String?role,???final?HttpServletRequest?request)?throws?ServletException?{??final?Claims?claims?=?(Claims)?request.getAttribute("claims");??return?((List{{greeting}}
?Success!?Welcome?{{userName}}
?????(logout)???

效果


原文鏈接:https://blog.csdn.net/change_on/article/details/76279441
版權(quán)聲明:本文為CSDN博主「J_小浩子」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。







關(guān)注Java技術(shù)棧看更多干貨


