Table of Contents
1. Log in
1.1 Interface description
1.2 JWT
1.3 Controller
1.4 Service
1.5 Login parameters, redis configuration, unified error code
1.6 Testing
2. Obtain user information
2.1 Interface description
2.2 Controller
2.3 Service
2.4 LoginUserVo
2.5 Testing
3. Log out
3.1 Interface description
3.2 Controller
3.3 Service
3.4 Testing
1. Login
1.1 Interface Description
Interface url:/login
Request method: POST
Request parameters:
parameter name |
Parameter Type |
illustrate |
account |
string |
account |
password |
string |
password |
Return data:
{ "success": true, "code": 200, "msg": "success", "data": "token" }
1.2 JWT
Login using JWT technology.
jwt can generate an encrypted token as the user’s login token. When the user logs in successfully, it will be issued to the client.
When requesting resources or interfaces that require login, the token is carried and the backend verifies whether the token is legal.
jwt consists of three parts: A, B, C
A: Header, {“type”:”JWT”,”alg”:”HS256″} fixed
B: playload, which stores information, such as user ID, expiration time, etc., can be decrypted, and cannot store sensitive information.
C: Visa, A and B are encrypted with secret key. As long as the secret key is not lost, it can be considered safe.
jwt verification is mainly to verify whether part C is legal.
Dependency packages:
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
Tools:
package com.pjp.blog.utils; import io.jsonwebtoken.Jwt; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; import java.util.HashMap; import java.util.Map; public class JWTUtils { //defined key private static final String jwtToken = "123456PJP!@###$$"; public static String createToken(Long userId) { //Part B Map<String, Object> claims = new HashMap<>(); claims.put("userId", userId); JwtBuilder jwtBuilder = Jwts.builder() .signWith(SignatureAlgorithm.HS256, jwtToken) // Signing algorithm, the secret key is part A of jwtToken .setClaims(claims) // body data, must be unique, set by yourself .setIssuedAt(new Date()) // Set the issuance time to ensure that the Token generated each time is inconsistent .setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 60 * 1000)); //Expiration time is the valid time of one day String token = jwtBuilder.compact(); return token; } public static Map<String, Object> checkToken(String token) { try { Jwt parse = Jwts.parser().setSigningKey(jwtToken).parse(token); return (Map<String, Object>) parse.getBody(); } catch (Exception e) { e.printStackTrace(); } return null; } }
Test it in the current class
public static void main(String[] args) { String token = JWTUtils.createToken(1001l); System.out.println(token); Map<String, Object> map = JWTUtils.checkToken(token); System.out.println(map.get("userId")); }
output
1.3 Controller
@SysUserService is not used directly here because @SysUserService is only responsible for related operations of the SysUser table.
Create a new business specifically responsible for login to combine other services
package com.pjp.blog.controller; import com.pjp.blog.service.LoginService; import com.pjp.blog.vo.Result; import com.pjp.blog.vo.params.LoginParams; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("login") public class LoginController { @Autowired private LoginService loginService; @PostMapping public Result login(@RequestBody LoginParams loginParams){ return loginService.login(loginParams); } }
1.4 Service
public interface LoginService { /** * User login * @param loginParams * @return */ Result login(LoginParams loginParams); }
md5 encryption dependency package:
<dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> </dependency>
Pay attention to the guide package
LoginServiceImpl
Logic:
* 1.Check whether the parameters are legal
* 2.Query whether it exists in the user table based on the user name and password
* 3.If it does not exist, login fails
* 4.If it exists, use jwt to generate Token and return it to the front end
* 5.Put Token into redis, redis token: user information, set expiration time
* (When logging in and authenticating, first verify whether the toke string is legal, and then go to redis to verify whether it exists)
package com.pjp.blog.service.impl; import com.alibaba.fastjson.JSON; import com.pjp.blog.dao.pojo.SysUser; import com.pjp.blog.service.LoginService; import com.pjp.blog.service.SysUserService; import com.pjp.blog.utils.JWTUtils; import com.pjp.blog.vo.ErrorCode; import com.pjp.blog.vo.Result; import com.pjp.blog.vo.params.LoginParams; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; @Service public class LoginServiceImpl implements LoginService { //Define encryption salt // The variables modified by final are constants. The final keyword can be used to modify variables, and the modified variables are constants. // Once assigned, it cannot be modified again. The purpose of this is to ensure that the value of the variable will not be modified and avoid unexpected errors. private static final String slat = "pjp!@###"; @Autowired private SysUserService sysUserService; @Autowired private RedisTemplate<String, String> redisTemplate; @Override public Result login(LoginParams loginParams) { /** * 1. Check whether the parameters are legal * 2. Query whether it exists in the user table based on the user name and password. * 3. If it does not exist, login fails * 4. If it exists, use jwt to generate Token and return it to the front end * 5.Token is put into redis, redis token: user information sets expiration time * (When logging in and authenticating, first verify whether the toke string is legal, and then go to redis to verify whether it exists) */ String account = loginParams.getAccount(); String password = loginParams.getPassword(); if (StringUtils.isBlank(account) || StringUtils.isBlank(password)) { return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), ErrorCode.ACCOUNT_PWD_NOT_EXIST.getMsg()); } //Use md5 encryption String pwd = DigestUtils.md5Hex(password + slat); SysUser sysUser = sysUserService.findUser(account, pwd); if (sysUser == null) { return Result.fail(ErrorCode.ACCOUNT_PWD_NOT_EXIST.getCode(), ErrorCode.ACCOUNT_PWD_NOT_EXIST.getMsg()); } //Log in successfully, use JWT to generate token, return token and redis String token = JWTUtils.createToken(sysUser.getId()); //Put in redis and set the expiration time // JSON.toJSONString() redisTemplate.opsForValue().set("TOKEN_" + token, JSON.toJSONString(sysUser), 1, TimeUnit.DAYS); return Result.success(token); } }
SysUserService
public interface SysUserService { /** * Query users based on username and password * * @param account * @param password * @return */ SysUser findUser(String account, String password); }
SysUserServiceIml
package com.pjp.blog.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.pjp.blog.dao.mapper.SysUserMapper; import com.pjp.blog.dao.pojo.SysUser; import com.pjp.blog.service.SysUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class SysUserServiceIml implements SysUserService { @Autowired private SysUserMapper sysUserMapper; @Override public SysUser findUser(String account, String pwd) { LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>(); //queryWrapper.eq() - equal to queryWrapper.eq(SysUser::getAccount,account); queryWrapper.eq(SysUser::getPassword,pwd); queryWrapper.select(SysUser::getId,SysUser::getAccount,SysUser::getAvatar,SysUser::getNickname); //Anyway, there is only one query user. Add a limit to improve query efficiency. queryWrapper.last("limit 1"); return sysUserMapper.selectOne(queryWrapper); } }
1.5 login parameters, redis configuration, unified error code
Encapsulate the Login parameters of the receiving front end
@Data public class LoginParams { private String account; private String password; }
Uniform Error Code
package com.pjp.blog.vo; public enum ErrorCode { PARAMS_ERROR(10001, "Parameter is incorrect"), ACCOUNT_PWD_NOT_EXIST(10002, "Username or password does not exist"), NO_PERMISSION(70001, "No access rights"), SESSION_TIME_OUT(90001, "Session timeout"), NO_LOGIN(90002, "Not logged in"), TOKEN_ERROR(10003,"Token is illegal") ; private int code; private String msg; ErrorCode(int code, String msg) { this.code = code; this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
redis configuration
spring: redis: database: 0 ##View according to the command ifConfig in the virtual machine host: 192.168.88.129 password: 123456 port: 6379 jedis: pool: max-active: 8 min-idle: 0 max-idle: 8 max-wait: -1 timeout: 5000
1.6 Test
Use postman to test, because after logging in, you need to jump to the page and perform token authentication. If the interface is not written, there will be problems with the front end. After the token is obtained by the front end, it will be stored in storage h5, local storage
Open the redis service before testing
[root@192 ~]# cd opt/ [root@192 opt]#ll Total usage 2960 drwxrwxr-x. 8 root root 4096 July 10 19:39 redis-7.0.12 -rw-r--r--. 1 root root 3023189 August 9 16:57 redis-7.0.12.tar.gz [root@192 opt]# cd redis-7.0.12 [root@192 redis-7.0.12]# cd /myredis [root@192 myredis]# redis-server /myredis/redis7.0.12.conf [root@192 myredis]# ps -ef|grep redis|grep -v grep root 2898 1 0 10:52 ? 00:00:00 redis-server *:6379 [root@192 myredis]# redis-cli -a 123456 Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. 127.0.0.1:6379>quit #Turn off the firewall; [root@192 myredis]# systemctl stop firewalld.service #Disable firewall startup; [root@192 myredis]# systemctl disable firewalld.service Removed symlink /etc/systemd/system/multi-user.target.wants/firewalld.service. Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service. [root@192 myredis]#ll Total usage 112 -rw-r--r--. 1 root root 109 August 9 22:05 dump.rdb -rw-r--r--. 1 root root 106544 August 9 21:52 redis7.0.12.conf [root@192 myredis]# vim redis7.0.12.conf [root@192 myredis]# redis-server /myredis/redis7.0.12.conf [root@192 myredis]# ps -ef|grep redis|grep -v grep root 2898 1 0 10:52 ? 00:00:22 redis-server *:6379 [root@192 myredis]# redis-cli -a 123456 Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. 127.0.0.1:6379> keys * 1) "k1" 2) "TOKEN_eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2OTI1Mzk5MjcsInVzZXJJZCI6MSwiaWF0IjoxNjkxNjUwODk0fQ.0YLVIlnAfrRLx2sDAQjZy_3mM5tZa8Y2cwLtbGwo1eU" 127.0.0.1:6379>
Test using postman
Redis shutdown
##Single instance shutdown: redis-cli shutdown ## Or multiple instances are closed, and the specified port is closed: redis-cli -p 6379 shutdown ##Start redis redis-server /etc/redis.conf ## Check the process to see if it is started ps -ef|grep redis ##Access via client: redis-cli ##or redis-cli -p 6379
127.0.0.1:6379> SHUTDOWN not connected> quit [root@192 myredis]#
2. Obtain user information
2.1 Interface Description
Interface url:/users/currentUser
Request method: GET
Request parameters:
parameter name |
Parameter Type |
illustrate |
Authorization |
string |
Header information (TOKEN) |
Return data:
{ "success": true, "code": 200, "msg": "success", "data": { "id":1, "account":"admin", "nickaname":"pjp", "avatar":"http://localhost:8080/static/user/admin.png" } }
2.2 Controller
package com.pjp.blog.controller; import com.pjp.blog.service.SysUserService; import com.pjp.blog.vo.Result; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("users") public class UserController { @Autowired private SysUserService sysUserService; @GetMapping("currentUser") public Result currentUser(@RequestHeader("Authorization") String token) { return sysUserService.getUserInfoByToken(token); } }
2.3 Service
logic:
* 1.Token legality verification Whether it is empty and whether the parsing is successful Whether redis exists
* 2. If the verification fails, return an error
* 3. If successful, return the corresponding result LoginUserVo
SysUserService
public interface SysUserService { /** * Verify based on token and return user data * @param token * @return */ Result getUserInfoByToken(String token); }
SysUserServiceIml
@Service public class SysUserServiceIml implements SysUserService { @Autowired private TokenService tokenService; @Override public Result getUserInfoByToken(String token) { /** * 1.Token legality verification * Whether it is empty, whether the parsing is successful, whether redis exists * 2. If the verification fails, return an error * 3. If successful, return the corresponding result LoginUserVo */ SysUser sysUser = tokenService.checkToken(token); if(sysUser == null){ Result.fail(ErrorCode.TOKEN_ERROR.getCode(), ErrorCode.TOKEN_ERROR.getMsg()); } LoginUserVo loginUserVo = new LoginUserVo(); loginUserVo.setId(sysUser.getId()); loginUserVo.setNickname(sysUser.getNickname()); loginUserVo.setAvatar(sysUser.getAvatar()); loginUserVo.setAccount(sysUser.getAccount()); return Result.success(loginUserVo); } }
TokenService
public interface TokenService { /** * Verify token and return user information * @param token * @return */ SysUser checkToken(String token); }
TokenServiceImpl
package com.pjp.blog.service.impl; import com.alibaba.fastjson.JSON; import com.pjp.blog.dao.pojo.SysUser; import com.pjp.blog.service.TokenService; import com.pjp.blog.utils.JWTUtils; import com.pjp.blog.vo.ErrorCode; import com.pjp.blog.vo.Result; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.Map; @Service public class TokenServiceImpl implements TokenService { @Autowired private RedisTemplate<String, String> redisTemplate; @Override public SysUser checkToken(String token) { if (StringUtils.isBlank(token)) { return null; } Map<String, Object> map = JWTUtils.checkToken(token); if (map == null) { return null; } String userJson = redisTemplate.opsForValue().get("TOKEN_" + token); if(StringUtils.isBlank(userJson)){ return null; } //Parse with Json SysUser sysUser = JSON.parseObject(userJson, SysUser.class); return sysUser; } }
2.4 LoginUserVo
@Data public class LoginUserVo { private Long id; private String account; private String nickname; private String avatar; }
2.5 Test
3. Log out
3.1 Interface Description
Interface url:/logout
Request method: GET
Request parameters:
parameter name |
Parameter Type |
illustrate |
Authorization |
string |
Header information (TOKEN) |
Return data:
{ "success": true, "code": 200, "msg": "success", "data": null }
3.2 Controller
package com.pjp.blog.controller; @RestController @RequestMapping("/logout") public class LogoutController { @Autowired private LoginService loginService; @GetMapping public Result logout(@RequestHeader("Authorization") String token) { return loginService.logout(token); } }
3.3 Service
public interface LoginService { /** * sign out * @param token * @return */ Result logout(String token); }
@Service public class LoginServiceImpl implements LoginService { @Autowired private RedisTemplate<String, String> redisTemplate; @Override public Result logout(String token) { redisTemplate.delete("TOKEN_" + token); return Result.success(null); } }