Authentication center service
Meet JWT
json web token is an open standard. It defines a compact and self-contained way to safely transmit information between parties as json objects
- After authentication, the server will generate a json object and send it to the client. After that, both the client and the server need to bring this object to transmit data. The server fully recognizes the client's identity through this json object. In order to prevent tampering with data, the server will add a signature (encryption) when generating it. The server does not save session data, that is, stateless, More suitable for expansion
- Which environments can consider using jwt? User authorization, information exchange
JWT components
Header: header information
The Header consists of two parts (Token type, name of encryption algorithm) and uses base64 encoding
Payload: the data we want to transfer
Poetry data in the form of Payload KV, here is the information we want to transmit (Token information if authorized)
Signature: Signature
Signature in order to get a signature, we must first have an encoded header, an encoded payload and a key. The algorithm used for signing is the one specified in the header, and then they will be signed
We need a signature formula
HMACSHA245(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
Generate a signature, return a string and return it to the client. After that, the client will bring this string for authentication every time it accesses
JWT uses. Number to connect HHH.PPPP.SSSS
Authorization, authentication design
We will not consider the gateway here, but will build it later. We will focus on the middle and right parts
In the authentication part, we independently implement the public tool class. Why? The following three points
- JWT is essentially an encrypted string calculated by the algorithm, which can also be inversely parsed by the algorithm. It does not rely on any framework, so this function has the premise that it can be extracted separately
- Our e-commerce system contains multiple micro services. Obviously, each service needs authentication, so we extract this method for reuse
- For high-performance authentication, why not do authentication in the authorization center? First, he turns back to a series of operations such as http requests. If we only use java locally, many steps are omitted, and the performance is multiplied
Implementation of authorization code
We create a new service to write our authentication center
e-commerce-authority-center
Import related dependencies
<dependencies> <!-- spring cloud alibaba nacos discovery rely on --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>2.2.3.RELEASE</version> </dependency> <!-- Java Persistence API, ORM standard --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- MySQL drive, be careful, This need and MySQL Version correspondence --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.12</version> <scope>runtime</scope> </dependency> <dependency> <groupId>com.hyc.ecommerce</groupId> <artifactId>e-commerce-mvc-config</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- zipkin = spring-cloud-starter-sleuth + spring-cloud-sleuth-zipkin--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency> <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> <version>2.5.0.RELEASE</version> </dependency> <!-- screw Generate database document --> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency> <dependency> <groupId>cn.smallbun.screw</groupId> <artifactId>screw-core</artifactId> <version>1.0.3</version> </dependency> </dependencies>
After importing the dependency, we write the corresponding configuration, such as registering with naocs, joining the supervision of adminserver, configuring data sources, etc. Here we use jpa to do orm
Configuration writing
server: port: 7000 servlet: context-path: /ecommerce-authority-center spring: application: name: e-commerce-authority-center cloud: nacos: discovery: enabled: true # If you do not want to use Nacos for service registration and discovery, set it to false server-addr: 127.0.0.1:8848 # Nacos server address # server-addr: 127.0.0.1:8848,127.0.0.1:8849,127.0.0.1:8850 # Nacos server address 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: # data source 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 # Connection pool 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 # The default is web base-url: http://127.0.0.1:9411/ # Exposure endpoint management: endpoints: web: exposure: include: '*' endpoint: health: show-details: always
After the configuration is completed, write the main startup class @ EnableJpaAuditing. Because we use the automatic join creation time and modification time, we need to turn on the automatic audit function of jpa, otherwise an error will be reported
@EnableJpaAuditing //Allow automatic audit of jpa @SpringBootApplication @EnableDiscoveryClient public class AuthorityApplication { public static void main(String[] args) { SpringApplication.run(AuthorityApplication.class, args); } }
Whether the test environment is correct under the test package
/** * Authorization center test entrance * Verify the availability of the authorization center environment */ @SpringBootTest @RunWith(SpringRunner.class) public class AuthorityCenterApplicationTest { @Test public void conetextLoad() { } }
After the environment is ok
Let's test whether the database operation is available
Write entity class ecommerceUser
/* * User table entity class definition * */ @Entity @EntityListeners(AuditingEntityListener.class) @Table(name = "t_ecommerce_user") @Data @NoArgsConstructor @AllArgsConstructor public class EcommerceUser { /* Self increasing assembly*/ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private long id; /*user name*/ @Column(name = "username", nullable = false) private String username; /* MD5 password*/ @Column(name = "password", nullable = false) private String password; /*Additional information json string storage*/ @Column(name = "extra_info", nullable = false) private String extraInfo; /*Automatically add the annotation of the main startup class required for the creation time*/ @CreatedDate @Column(name = "create_time", nullable = false) private Date createTime; /*Adding the update time automatically requires the annotation of the main startup class*/ @CreatedDate @Column(name = "update_time", nullable = false) private Date updateTime; }
With entity classes, we need to implement data operations, so we write Dao interfaces
In fact, when we create the interface, jpa already has the corresponding basic addition, deletion, modification and query methods
Here we implement two custom query methods
/** * EcommerceUserDao Interface definition */ public interface EcommerceUserDao extends JpaRepository<EcommerceUser, Long> { /* * Query EcommerceUser object by user name * Equal to select * form t_ecommerce_user where username=? * */ EcommerceUser findByUsername(String name); /* * Query EcommerceUser object by user name * Equal to select * form t_ecommerce_user where username=? and password=? * */ EcommerceUser findByUsernameAndPassword(String name, String password); }
Then create the test service
/** * @author : Leng Huanyuan * @date : 2021/12/4 * @context: EcommerceUser Related tests * @params : null * @return : * @return : null */ @SpringBootTest @RunWith(SpringRunner.class) @Slf4j public class EcommerUserTest { @Autowired EcommerceUserDao ecommerceUserDao; /*Add a user data to the test */ @Test public void createUserRecord() { EcommerceUser ecommerceUser = new EcommerceUser(); //Set information to insert ecommerceUser.setUsername("hyc@qq.com"); ecommerceUser.setPassword(MD5.create().digestHex("123456")); ecommerceUser.setExtraInfo("{}"); //Log print return results log.info("server user:[{}]", JSON.toJSON(ecommerceUserDao.save(ecommerceUser))); } /*Test the custom method we wrote to query the new role we just created*/ @Test public void SelectUserInfo() { String username = "hyc@qq.com"; log.info("select userinof:[{}]", JSON.toJSON(ecommerceUserDao.findByUsername(username))); } }
Test related methods: add users or query users according to conditions, and the tests pass
Asymmetric encryption algorithm for generating public and private keys of RSA256
He completes the verification by encrypting the private key and decrypting the public key. At present, many authentication algorithms are encrypted and authenticated by JWTRSA256. If you don't know much, you can use RSA256
code
Write a test class for generating public key and create some commonly used VO objects to store some commonly used variables, such as user information, public key, key and some commonly used attributes into the VO model
@Slf4j @SpringBootTest @RunWith(SpringRunner.class) /** * * @author : Leng Huanyuan * @date : 2021/12/5 * @context: RSA Asymmetric encryption algorithm * @params : null * @return : * @return : null */ public class RSATest { @Test public void generateKeyBytes() throws Exception { /*Get RSA algorithm instance*/ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); /* The minimum value here is 2048. If it is lower than, an error will be reported*/ keyPairGenerator.initialize(2048); /* * Generate public key pair * */ KeyPair keyPair = keyPairGenerator.generateKeyPair(); /*Get public and private key objects*/ 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())); } }
Create VO objects to save values and objects that we often use and will not change
The stored private key should be a private key, so it is only exposed to the authentication center. Therefore, we create a Constant package in the authentication service and create this AuthotityConstant class to save information
/** * @author : Leng Huanyuan * @date : 2021/12/5 * @context: Constant of authentication * @params : null * @return : * @return : null */ public class AuthorCanstant { /*The private key is only exposed to the authentication center and not to any other service*/ 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=="; /*The default token timeout is one day*/ public static final Integer DEFAULT_EXPIRE_DAY = 1; }
Then create some common VO models e-commerce-common
After saving the public key to the public package, our services need to be authorized and used
/** * @author : Leng Huanyuan * @date : 2021/12/5 * @context: Constant definition of general module * @params : null * @return : * @return : null */ public class CommonCanstant { /* RSA Public key*/ public static final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjF60ArnWpShyZ" + "R9emB6/ZiOf8cmJ5KebtoAGzBX9sQBwMspGJosST9wqhtmvLVMIiY40yCVz/u/Sc4I4IFWwWk2pK743hV8/RzT9Q+F" + "w+i4vrEVY5zdYgVMCBUcYhTdhDiX5QPW6nkCwIHvy4ALtOst43Sy5XiRe8g0bjB2Hnaj5fQCWuUXSZrdDoAqFTqmnA" + "e6BpBQhSEptAsI34KtOp8oolCxmTVbfLoQ8WQBOD4tpZn98oIFwgR/QLVP9l3csVPXGkCTgG2BsXf5gEU/zYrwY0qqO" + "tMHAZ5LSPm6KFGwykQW1kcb32KilhMDzsMZSVAd5d6XC2ZvOul5d6rVTFwIDAQAB"; /* JWT Store user information in key*/ public static final String JWT_USER_INFO_KEY = "e-commerce-user"; /*Service ID of authorization center*/ public static final String AUTHORITY_CENTER_SERVICE_ID = "e-commerce-authity-center"; }
Common VO objects of user information
- JwtToken
- LoginUserinfo
- UsernameAndPassword
/** * @author : Leng Huanyuan * @date : 2021/12/5 * @context: The token given to the client after authentication by the authorization center * @params : null * @return : * @return : null */ @Data @NoArgsConstructor @AllArgsConstructor public class JwtToken { /* JWT*/ private String token; }
@Data @NoArgsConstructor @AllArgsConstructor public class LoginUserinfo { /*User id*/ private Long id; /*user name*/ private String username; }
/** * @author : Leng Huanyuan * @date : 2021/12/5 * @context:User name and password * @params : null * @return : * @return : null */ @Data @AllArgsConstructor @NoArgsConstructor public class UsernameAndPassword { /*user name */ private String username; /*password*/ private String password; }
Authorization service writing
First, create an interface IJWTService
Define the authorization method we need to implement
/** * @author : Leng Huanyuan * @date : 2021/12/5 * @context: JWT Related service interface definitions * @params : null * @return : * @return : null */ public interface IJWTService { /* * The default timeout is used to generate the token * */ String generateToken(String username, String password) throws Exception; /* * Timeout time can be set for generating JWT Token. The unit is days * */ String generateToken(String username, String password, Integer expireTime) throws Exception; /* * Register the user and generate a token return * */ String registerUserAndGenerateToken(UsernameAndPassword usernameAndPassword) throws Exception; }
Authorization method implementation class
Here we have three ways to implement it
- Generate token for default timeout
- Generate a token by customizing the timeout setting
- Register a new user and return the generated token
JWT object generation details:
1) We need to set the object to be passed
2) We need to set a non duplicate id
3) We need to set the timeout
4) Set up our encrypted signature
5) Return string object after setting
Jwts.builder() //claim here is actually jwt's payload object -- > kV .claim(CommonCanstant.JWT_USER_INFO_KEY, JSON.toJSONString(loginUserinfo)) // jwt id indicates the id of jwt .setId(UUID.randomUUID().toString()) //Expiration time of jwt .setExpiration(expireDate) // Here is to set the private key and encryption type of encryption .signWith(getPrivateKey(), SignatureAlgorithm.RS256) //The generated jwt information returns a string type .compact(); }
Complete code
@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 { //First, you need to verify whether the user has passed the authorization verification, that is, whether the entered user name and password can find the records matching the data table EcommerceUser ecommerceUser = ecommerceUserDao.findByUsernameAndPassword(username, password); if (ecommerceUser == null) { log.error("can not find user: [{}],[{}]", username, password); return null; } //Insert the object into the Token, that is, the object stored in JWT. The back end can know the user is operating after getting this information LoginUserinfo loginUserinfo = new LoginUserinfo( ecommerceUser.getId(), ecommerceUser.getUsername() ); if (expireTime <= 0) { expireTime = AuthorCanstant.DEFAULT_EXPIRE_DAY; } //Calculate timeout ZonedDateTime zdt = LocalDate.now().plus(expireTime, ChronoUnit.DAYS) .atStartOfDay(ZoneId.systemDefault()); Date expireDate = Date.from(zdt.toInstant()); return Jwts.builder() //claim here is actually jwt's payload object -- > kV .claim(CommonCanstant.JWT_USER_INFO_KEY, JSON.toJSONString(loginUserinfo)) // jwt id indicates the id of jwt .setId(UUID.randomUUID().toString()) //Expiration time of jwt .setExpiration(expireDate) // Here is to set the private key and encryption type of encryption .signWith(getPrivateKey(), SignatureAlgorithm.RS256) //The generated jwt information returns a string type .compact(); } @Override public String registerUserAndGenerateToken(UsernameAndPassword usernameAndPassword) throws Exception { //First check whether the user name exists. If it exists, it cannot be registered again 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()); //After MD5 coding ecommerceUser.setExtraInfo("{}"); //Register a new user and write it to a record table ecommerceUser = ecommerceUserDao.save(ecommerceUser); log.info("regiter user success:[{}],[{}]", ecommerceUser.getUsername()); //Generate a token and return return generateToken(ecommerceUser.getUsername(), ecommerceUser.getPassword()); } /* * Obtain the PrivateKey object according to the locally stored private key * */ private PrivateKey getPrivateKey() throws Exception { //Create a new pkcs8encoded keyspec with the given encoding key. PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(AuthorCanstant.PRIVATE_KEY)); // Set the factory encryption method for generating new keys KeyFactory keyFactory = KeyFactory.getInstance("RSA"); //Return the generated key return keyFactory.generatePrivate(priPKCS8); } }
After that, our authorization will use the above methods
Controller
We need to give a program entry for registering users and generating token s
It's our AuthorityController. The annotation @ IgnoreResponseAdvice we used before can be used here. Why don't we let it be encapsulated? We need to verify it. A simple JwtToken object can be used without encapsulation and transformation
@Slf4j @RestController @RequestMapping("/authority") public class AuthorityConroller { private final IJWTService ljwtService; public AuthorityConroller(IJWTService ljwtService) { this.ljwtService = ljwtService; } /* * The package that obtains the token (actually the login function) from the authorization center and there is no unified response in the returned information * */ @IgnoreResponseAdvice @PostMapping("/token") public JwtToken token(@RequestBody UsernameAndPassword usernameAndPassword) throws Exception { //Usually, the log does not print the user's information to prevent disclosure. We are an authorized server and are not open to the public, so we can print the user's information to the log for easy viewing log.info("request to get token with param:[{}]", JSON.toJSONString(usernameAndPassword)); return new JwtToken(ljwtService.generateToken( usernameAndPassword.getUsername(), usernameAndPassword.getPassword())); } /*Registering a user and returning the token to register the current user is a common user through the authorization center*/ @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)); } }
Implementation of authentication coding
Here, we put authentication in the public module. Why? Here, we need to use authentication services not only in the authentication center, but also in other services. With the idea of encapsulation, we extract public methods and put them in Common
Create JWT Token parsing class TokenParseUtil
/** * @author : Leng Huanyuan * @date : 2021/12/5 * @context: JWT Token Parsing tool class * @params : null * @return : * @return : null */ public class TokenParseUtil { public static LoginUserinfo parseUserInfoFromToken(String token) throws Exception { if (null == token) { return null; } Jws<Claims> claimsJws = parseToken(token, getPublicKey()); Claims body = claimsJws.getBody(); //If the Token has expired, null is returned if (body.getExpiration().before(Calendar.getInstance().getTime())) { return null; } // Returns the user information saved in the Token return JSON.parseObject( body.get(CommonCanstant.JWT_USER_INFO_KEY).toString(), LoginUserinfo.class ); } /* * Resolve JWT Token through public key * */ private static Jws<Claims> parseToken(String token, PublicKey publicKey) { // Use the set signature public key to parse the claims information token return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token); } /* * Get getPublicKey according to the locally stored public key * */ public static PublicKey getPublicKey() throws Exception { //Decoder we set the decoder to put the public key in X509EncodedKeySpec keySpec = new X509EncodedKeySpec( new BASE64Decoder().decodeBuffer(CommonCanstant.PUBLIC_KEY) ); //Create an RSA instance and generate a public key object through the example return KeyFactory.getInstance("RSA").generatePublic(keySpec); } }
Here is a problem. If the token is not a jwt token object, it will run out of exception without revealing the truth,
In fact, the problem here is not tenable. It should be that you have not passed in the token object. It is correct for us to throw an exception here, and it will not affect other services. Then, we can realize exception restart with sentinel and brother Hao pig. Here, we will not write a bottom-up method to analyze jwt token.
Authentication authorization
We write a test class to test whether the authorization and authentication objects are valid
/** * @author : Leng Huanyuan * @date : 2021/12/5 * @context: JWT Related test classes * @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( "hyc@qq.com", "e10adc3949ba59abbe56e057f20f883e" ); log.info("jwt token is:[{}]", jwtToken); LoginUserinfo userinfo = TokenParseUtil.parseUserInfoFromToken(jwtToken); log.info("userinfo by jwt prase token :[{}]", JSON.toJSONString(userinfo)); } }
Start the test and view the results
eyJhbGciOiJSUzI1NiJ9 .eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjExLFwidXNlcm5hbWVcIjpcImh5Y0BxcS5jb21cIn0iLCJqdGkiOiIzNDgwNjdjMi00MTBlLTQ3MjItYmM3ZS02NWQyYmNmYTRkN2MiLCJleHAiOjE2Mzg3MjAwMDB9 .ZbFl81MkIipJSULZLf4F2X2Fb0q1TwhHIMT7nyZsZVwUxXyZnK54RlzoGM_b-kMUdKO_Tab-qEeOT6Jn--FiKmbOziWXiBx3a-k5ipthMJx0Fez-X8Acty-Pg7zukNalugiLxGb5ophQoVQWRTDmv2hytGHqiV71HVyErznkJa36QQr6QsjXqlJleo3BBt-6BFzdTFPLUmdTEJ4XsmZBa_acUDGBhY0_tU2gYtKBWhwvMCknuyCcV-_GVI5EvgMIKRpeFSZrWfTsDG2y1MFcyzjKE6jnzek-YwT3XkzQ8eGzUbiOlaU_Zx5OJah-UtrKwqlAw9WbO71pNgEBefdsYw
As like as two peas, the JWT Token is packaged here. We can see that three points separately split header and payload and the signature is exactly the same as the structure we mentioned before.
userinfo by jwt prase token :[{"id":11,"username":"hyc@qq.com"}]
Get the objects we need to pass in jwt
Verify whether the interface provided externally is easy to use
Here we write an http script to test whether the interface provided by the external topic is useful
Token method
### Get Token -- implementation of login function POST http://127.0.0.1:7000/ecommerce-authority-center/authority/token Content-Type: application/json { "username": "hyc@qq.com", "password": "e10adc3949ba59abbe56e057f20f883e" }
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/token HTTP/1.1 200 Content-Type: application/json Transfer-Encoding: chunked Date: Sun, 05 Dec 2021 15:35:52 GMT Keep-Alive: timeout=60 Connection: keep-alive { "token": "eyJhbGciOiJSUzI1NiJ9.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjExLFwidXNlcm5hbWVcIjpcImh5Y0BxcS5jb21cIn0iLCJqdGkiOiIxNDU1M2FjZi1lZmE5LTQ4OTgtOTliYS1hNzA4NWI4MjU4MzAiLCJleHAiOjE2Mzg3MjAwMDB9.AlOpo6uf97R20ZLojXeun-3MK8DpSYlWxEygvDrtQeWaM9R0iKx-iW1VXnK6WoEntvqPxIrmPA7khjl3dXPa8kQHtdq-LVO7BDuZZDiQyZ64ZS7A9jWZr5JReSWBUSR1YUnsOvBRMkx4JVcAF3_W7nHwd722FFzOZRCr72hLHQIKpsugKtqjMEtaiEW0vcqphCYRJTAO_rQx1Lb1eVVg_Ufur0qSlKkV5dSJ0x3x9mc9UZRckwN0rrP7wQxZcrxJvKTfX7CkRRSO-CxZbG4WLokSaMtaGBMWU-7KGq7HSCZ0yuOgbbLdouHncsp6VD2tNLFdWSdJ_whCIbZxfX8R7w" }
token obtained successfully
Here he was not responded to the package, which proves that our previous selection shielding annotation has also taken effect, which is in line with our expectations
Verify if the record data table does not return null
### Get Token -- implementation of login function POST http://127.0.0.1:7000/ecommerce-authority-center/authority/token Content-Type: application/json ### Casually written id { "username": "hyc1111@qq.com", "password": "e10adc3949ba59abbe56e057f20f883e" }
The returned result also meets our expectation and is null
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/token HTTP/1.1 200 Content-Type: application/json Transfer-Encoding: chunked Date: Sun, 05 Dec 2021 15:40:44 GMT Keep-Alive: timeout=60 Connection: keep-alive { "token": null }
register
### Register user and return Token -- implementation of registration function POST http://127.0.0.1:7000/ecommerce-authority-center/authority/register Content-Type: application/json { "username": "hyc@qq.com", "password": "e10adc3949ba59abbe56e057f20f883e" }
This user has been registered before. Let's see if the expected processing will be returned
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/register HTTP/1.1 200 Content-Type: application/json Transfer-Encoding: chunked Date: Sun, 05 Dec 2021 15:42:00 GMT Keep-Alive: timeout=60 Connection: keep-alive { "token": null }
Now let's register a new user
### Register user and return Token -- implementation of registration function POST http://127.0.0.1:7000/ecommerce-authority-center/authority/register Content-Type: application/json { "username": "hyc11@qq.com", "password": "e10adc3949ba59abbe56e057f20f883e" }
It meets the expected results and creates our expected objects. At this time, let's take a look at the data table
POST http://127.0.0.1:7000/ecommerce-authority-center/authority/register HTTP/1.1 200 Content-Type: application/json Transfer-Encoding: chunked Date: Sun, 05 Dec 2021 15:42:57 GMT Keep-Alive: timeout=60 Connection: keep-alive { "token": "eyJhbGciOiJSUzI1NiJ9.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjEyLFwidXNlcm5hbWVcIjpcImh5YzExQHFxLmNvbVwifSIsImp0aSI6IjMxNDc0NmIwLTMyOGYtNDZkNS05ZTIwLTg3YjI0OWY1ZjZkOCIsImV4cCI6MTYzODcyMDAwMH0.MKxk-Q4BG5kaYFAsLiy13trtk_gDFmCKORpdE4EAwgSVecXFQcYfT1VvqSAKvoQLFsSlQAxOR5elV8CFOoKwAomwqdyyghZp63NKJ2smRbg3Y-4jWBzFVsUgcjOY2fwh7oNTdHEsWmLBYAh5r0hm_MysZsUEsE-cwb3sw8NSMk1OZp0J6tcRras7V1Uw5xXH8OnCoq2cUfdynJMHS29EzJT1TFPb8unVQ_A1RWodsHdK3n1Bl4wFbJjMtnHx7vzOeAUSNJx1XpAGdo0xYHK6HBpS9E1KBS3x1AnYFONM0DKd4-_QxMkBW1kkg2uWrRpf3GYZF20FKxXgmBAPHGZhew" }
Object generation, function verification, everything is normal
Summary of authentication service center
Compare Token based and server based authentication
Tradition:
- In the most traditional way, the Session id is used by the client to store cookie s, and the Session id is stored by the server
- Session means that every time a user passes the authentication, the server needs to create a record to save the user information, which is usually in memory (or in redis). As more and more users pass the authentication, the cost of the server here will increase
- When switching between different domain names, the request may be prohibited, that is, cross the problem
Based on token
- The difference between JWT and Session is that they both store user information. However, the Session is on the server side, while JWT is on the client side
- The JWT method disperses the user state to the client, which can significantly reduce the memory pressure of the server. The server only needs to parse the client's token with an algorithm to get the information
Comparison of the advantages and disadvantages of the two
- Parsing method: JWT directly parses the user information using the algorithm; Session requires additional data mapping. Realize matching
- Management method: JWT only has the limit of expiration time, and the Session data is saved in the server, which is more controllable
- Cross platform: JWT is a string that can be propagated arbitrarily. The cross platform of Session requires a unified parsing platform, which is cumbersome
- Timeliness: once JWT is generated and exists independently, it is difficult to achieve special control; Session timeliness has the final say of the logic of the server side.
TIPS: each has its own advantages and disadvantages. They are both login and authorization solutions