The SpringBoot Mybatis -plus logic is removed and automatically filled with default values
One, foreword
- Encapsulate returned results, error codes, prompt messages, and returned data.
- Unified exception interception: Intercepts manually thrown exceptions or the exceptions thrown by the system and returns data to the front end in a unified manner.
- JWT(Json Web Token) is an interface Token. After a successful login, the Token is returned to the front end, which saves the Token. All subsequent requests are sent back to the server, which verifies the Token to decide whether to release the Token. In addition, information such as user ID and user name can be saved in the token.
- The code uses Mybatis-Plus, you can see my previous article
2. Directory structure
Maven dependency packages
Add the following dependencies to POM.xml Dependencies
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.6.5</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
<version>5.3.3. RELEASE</version>
</dependency>
Copy the code
- JJWT on Java Json Web Token
- Hutool-all encapsulates many useful Java tool classes, highly recommended, very easy to use, official website address: www.hutool.cn/
- Spring-security-crypto password encryption and decoding tool
Four, encapsulation unified return results
CommonResult class
package com.llh.springbootdemo.config;
/ * * *@author llh
*/
public class CommonResult<T> {
private Integer code;
private String msg;
private T data;
public CommonResult(int code, String msg) {
this.code = code;
this.msg = msg;
}
public CommonResult(int code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static <T> CommonResult<T> success(T t) {
return new CommonResult<T>(200."Operation successful", t);
}
public static <T> CommonResult<T> error(T t) {
return new CommonResult<T>(300."Operation failed", t);
}
public Integer getCode(a) {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg(a) {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData(a) {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString(a) {
return "CommonResult{" +
"code=" + code +
", msg='" + msg + '\' ' +
", data=" + data +
'} '; }}Copy the code
- use
@PostMapping("/register")
public CommonResult<Boolean> register(@RequestBody UserInfo userInfo) {
return CommonResult.success(userInfoService.register(userInfo));
}
@PostMapping("/login")
public CommonResult<String> login(@RequestBody UserInfo userInfo) {
return CommonResult.success(userInfoService.login(userInfo));
}
Copy the code
The result is as follows:
{
"code": 200."msg": "Operation successful"."data": true
}
Copy the code
5. Unified exception interception
GlobalExceptionHandler class
package com.llh.springbootdemo.config;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/ * * *@author llh
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public CommonResult<String> exceptionHandler(Exception e) {
returnCommonResult.error(e.getMessage()); }}Copy the code
- Only Exception handling is done here, and you can add custom Exception handling.
Vi. Registration function
PasswordEncoder#encode encrypts the password. The encryption is asymmetric, that is, the same password is encrypted after the string is different.
@Override public Boolean register(UserInfo userInfo) { List<UserInfo> selectedList = list(new LambdaQueryWrapper<UserInfo>() .eq(UserInfo::getUsername, userInfo.getUsername())); if (! Selectedlist.isempty ()) {throw new RuntimeException(" Registration failed, the user name already exists "); } // PasswordEncoder PasswordEncoder = new BCryptPasswordEncoder(); String encodedPassword = passwordEncoder.encode(userInfo.getPassword()); userInfo.setPassword(encodedPassword); return save(userInfo); }Copy the code
7. Login function
PasswordEncoder#matches verifies the password
@Override
public String login(UserInfo userInfo) {
List<UserInfo> selectedList = list(new LambdaQueryWrapper<UserInfo>()
.eq(UserInfo::getUsername, userInfo.getUsername()));
if (selectedList.isEmpty()) {
throw new RuntimeException("Login failed. Account does not exist.");
}
UserInfo selected = selectedList.get(0);
String encodedPassword = selected.getPassword();
// Check whether the password is correct
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
boolean result = passwordEncoder.matches(userInfo.getPassword(), encodedPassword);
if(! result) {throw new RuntimeException("Login failed, user password incorrect");
}
// Generate a token
HashMap<String, Object> map = new HashMap<>(2);
map.put("userId", selected.getId());
String token = JwtUtil.generateToken(map);
return token;
}
Copy the code
JWT generation and validation tool classes
JwtUtil class
package com.llh.springbootdemo.utils; import cn.hutool.core.date.DateUtil; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; import java.util.Map; /** * @author LLH */ public class JwtUtil {/** * private static final String SECRET = "token_secret"; /** * token prefix */ private static final String TOKEN_PREFIX = "Bearer"; /** * token expiration time */ private static final Integer EXPIRE_SECONDS = 60 * 60 * 24 * 7; Public static String generateToken(Map<String, Object> map) { String jwt = Jwts.builder() .setSubject("user info").setClaims(map) .signWith(SignatureAlgorithm.HS512, SECRET) .setExpiration(DateUtil.offsetSecond(new Date(), EXPIRE_SECONDS)) .compact(); return TOKEN_PREFIX + "_" + jwt; } public static Map<String, Object> resolveToken(String token) {if (token == null) {throw new RuntimeException(" token empty "); } try { return Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token.replaceFirst(TOKEN_PREFIX + "_", "")) .getBody(); } catch (ExpiredJwtException e) {throw new RuntimeException(" token has expired "); } catch (Exception e) {throw new RuntimeException(" token parsing Exception "); }}}Copy the code
9. Unified request interception
Intercepts all requests into the interceptor, retrieves the token from the request header, parses the token, and saves the user ID into the context object
TokenInterceptor class TokenInterceptor
package com.llh.springbootdemo.config;
import com.llh.springbootdemo.utils.JwtUtil;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/ * * *@author llh
*/
public class TokenInterceptor implements HandlerInterceptor {
/** * request header */
private static final String HEADER_AUTH = "Authorization";
/** * secure URL, no token */ required
private static final List<String> SAFE_URL_LIST = Arrays.asList("/userInfo/login"."/userInfo/register");
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
response.setContentType("application/json; charset=utf-8");
String url = request.getRequestURI().substring(request.getContextPath().length());
System.out.println(url);
// Requests such as login and registration do not require tokens
if (SAFE_URL_LIST.contains(url)) {
return true;
}
// Read the token from the request header
String token = request.getHeader(HEADER_AUTH);
if (token == null) {
throw new RuntimeException("Request failed, token is empty");
}
// Parse the token
Map<String, Object> map = JwtUtil.resolveToken(token);
Long userId = Long.parseLong(map.get("userId").toString());
ContextHolder.setUserId(userId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ContextHolder.shutdown(); }}Copy the code
- All requests were approved
preHandle
methods ContextHolder.setUserId(userId);
The request begins to place the parsed user ID into the context object.ContextHolder.shutdown();
End of request culls the user ID from the context object.
The WebMvcConfiguration class adds an interceptor to the MVC configuration
package com.llh.springbootdemo.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; /** * @author llh */ @Configuration public class WebMvcConfiguration extends WebMvcConfigurationSupport { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new TokenInterceptor()); }}Copy the code
ContextHolder Class Context object class
package com.llh.springbootdemo.config;
/ * * *@author llh
*/
public class ContextHolder {
public static ThreadLocal<Long> context = new ThreadLocal<>();
public static void setUserId(Long userId) {
context.set(userId);
}
public static Long getUserId(a) {
return context.get();
}
public static void shutdown(a) { context.remove(); }}Copy the code
- The main use is ThreadLocal, where context objects can be retrieved from any thread requesting them.
- For example, changing the password
Long userId = ContextHolder.getUserId();
Get the user ID. - Change the password to update the data according to the user ID. The user ID is directly taken from the context object, so that you do not need to pass it from the front. If you pass it from the front, it means that anyone can change the password of others, which is very insecure.
- Take it from the context, that is, from the token, and you’re just protecting the interface, you can only manipulate your own data.
@Override
public Boolean changePassword(UserInfo userInfo) {
// Password encryption
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encodedPassword = passwordEncoder.encode(userInfo.getPassword());
UserInfo updateUserInfo = new UserInfo();
updateUserInfo.setPassword(encodedPassword);
// Get the user ID from the context object, not from the user
Long userId = ContextHolder.getUserId();
updateUserInfo.setId(userId);
return updateById(updateUserInfo);
}
Copy the code
X. Complete code
UserInfoService
package com.llh.springbootdemo.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.llh.springbootdemo.entity.UserInfo;
/ * * *@author llh
*/
public interface UserInfoService extends IService<UserInfo> {
/** * Register **@paramUserInfo Registration information *@returnSuccessful */
Boolean register(UserInfo userInfo);
/** * login **@paramUserInfo Login information *@returnTokens * /
String login(UserInfo userInfo);
/** * change the password **@paramUserInfo User information *@returnSuccessful */
Boolean changePassword(UserInfo userInfo);
}
Copy the code
UserInfoServiceImpl
package com.llh.springbootdemo.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.llh.springbootdemo.config.ContextHolder;
import com.llh.springbootdemo.entity.UserInfo;
import com.llh.springbootdemo.mapper.UserInfoMapper;
import com.llh.springbootdemo.service.UserInfoService;
import com.llh.springbootdemo.utils.JwtUtil;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
/ * * *@author llh
*/
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper.UserInfo> implements UserInfoService {
@Override
public Boolean register(UserInfo userInfo) {
List<UserInfo> selectedList = list(new LambdaQueryWrapper<UserInfo>()
.eq(UserInfo::getUsername, userInfo.getUsername()));
if(! selectedList.isEmpty()) {throw new RuntimeException("Registration failed, the user name already exists");
}
// Password encryption
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encodedPassword = passwordEncoder.encode(userInfo.getPassword());
userInfo.setPassword(encodedPassword);
return save(userInfo);
}
@Override
public String login(UserInfo userInfo) {
List<UserInfo> selectedList = list(new LambdaQueryWrapper<UserInfo>()
.eq(UserInfo::getUsername, userInfo.getUsername()));
if (selectedList.isEmpty()) {
throw new RuntimeException("Login failed. Account does not exist.");
}
UserInfo selected = selectedList.get(0);
String encodedPassword = selected.getPassword();
// Check whether the password is correct
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
boolean result = passwordEncoder.matches(userInfo.getPassword(), encodedPassword);
if(! result) {throw new RuntimeException("Login failed, user password incorrect");
}
// Generate a token
HashMap<String, Object> map = new HashMap<>(2);
map.put("userId", selected.getId());
String token = JwtUtil.generateToken(map);
return token;
}
@Override
public Boolean changePassword(UserInfo userInfo) {
// Password encryption
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encodedPassword = passwordEncoder.encode(userInfo.getPassword());
UserInfo updateUserInfo = new UserInfo();
updateUserInfo.setPassword(encodedPassword);
// Get the user ID from the context object, not from the user
Long userId = ContextHolder.getUserId();
updateUserInfo.setId(userId);
returnupdateById(updateUserInfo); }}Copy the code
UserInfoController
package com.llh.springbootdemo.controller;
import com.llh.springbootdemo.config.CommonResult;
import com.llh.springbootdemo.entity.UserInfo;
import com.llh.springbootdemo.service.UserInfoService;
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;
import javax.annotation.Resource;
/ * * *@author llh
*/
@RestController
@RequestMapping("/userInfo")
public class UserInfoController {
@Resource
private UserInfoService userInfoService;
@PostMapping("/register")
public CommonResult<Boolean> register(@RequestBody UserInfo userInfo) {
return CommonResult.success(userInfoService.register(userInfo));
}
@PostMapping("/login")
public CommonResult<String> login(@RequestBody UserInfo userInfo) {
return CommonResult.success(userInfoService.login(userInfo));
}
@PostMapping("/changePassword")
public CommonResult<Boolean> changePassword(@RequestBody UserInfo userInfo) {
returnCommonResult.success(userInfoService.changePassword(userInfo)); }}Copy the code
pom.xml
<project xmlns="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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2. RELEASE</version>
<relativePath/> <! -- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.llh</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0</version>
<name>spring-boot-demo</name>
<description>springboot project description</description>
<properties>
<mybatis-spring-boot.version>2.1.4</mybatis-spring-boot.version>
<mybatis-plus.version>3.4.2</mybatis-plus.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.6.5</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
<version>5.3.3. RELEASE</version>
</dependency>
</dependencies>
</project>
Copy the code
Xi. Test
11.1 registered
Check that the database password is encrypted
11.2 the login
Token returned after successful login
11.3 Changing a Password
You need to add a token to the request header Authorization
Change the password from 123456->12345678
Xii. Conclusion
Source code address: github.com/tigerleeli/…
Synchronize wechat official account: Little Tiger’s technology blog