Table of Contents
1. Environment setup
1. Database
2. Introduce dependencies
3. Configuration information
4. Create package structure and database entity class
2. Interface development-registration interface
Prerequisite preparation
response data
demand analysis
Global exception handling
Code writing
test
3. Interface development-login interface
Prerequisite preparation
response data
demand analysis
Code writing
test
Interceptor
test
1. Environment setup
1. Database
-- Create database create database big_event; -- use database use big_event; -- user table create table user ( id int unsigned primary key auto_increment comment 'ID', username varchar(20) not null unique comment 'username', password varchar(32) comment 'password', nickname varchar(10) default '' comment 'nickname', email varchar(128) default '' comment 'email', user_pic varchar(128) default '' comment 'avatar', create_time datetime not null comment 'Creation time', update_time datetime not null comment 'modification time' ) comment 'user table'; -- Classification table create table category( id int unsigned primary key auto_increment comment 'ID', category_name varchar(32) not null comment 'category name', category_alias varchar(32) not null comment 'category name', create_user int unsigned not null comment 'Creator ID', create_time datetime not null comment 'Creation time', update_time datetime not null comment 'modification time', constraint fk_category_user foreign key (create_user) references user(id) -- foreign key constraints ); -- Article table create table article( id int unsigned primary key auto_increment comment 'ID', title varchar(30) not null comment 'article title', content varchar(10000) not null comment 'article content', cover_img varchar(128) not null comment 'article cover', state varchar(3) default 'draft' comment 'Article status: can only be [published] or [draft]', category_id int unsigned comment 'Article category ID', create_user int unsigned not null comment 'Creator ID', create_time datetime not null comment 'Creation time', update_time datetime not null comment 'modification time', constraint fk_article_category foreign key (category_id) references category(id),-- Foreign key constraints constraint fk_article_user foreign key (create_user) references user(id) -- foreign key constraints )
2. Introduce dependencies
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.2</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
3. Configuration information
spring: datasource: driverClassName: com.mysql.cj.jdbc.Drive url: jdbc:mysql://localhost:3306/big_event username: root password: 123456 server: port: 8888
4. Create package structure and database entity class
2. Interface Development-Registration Interface
Prerequisite preparation
Let’s look at the user entity class first. The reason why we don’t set getters and setters for fields is because we can use the lombok tool for the beauty and simplicity of the code.
lombok: During the compilation phase, getters, setters, and toString are automatically generated for entity classes.
The method of use is to add the annotation @Data to the class
response data
In actual project development, a string or data will not be returned directly like the code written before. But there is a unified response result, that is, the Result class
@Data @NoArgsConstructor @AllArgsConstructor public class Result<T> { private Integer code;//Business status code 0-success 1-failure private String message;//prompt message private T data;//response data //Quickly return the successful response result of the operation (with response data) public static <E> Result<E> success(E data) { return new Result<>(0, "Operation successful", data); } //Quickly return the successful response result of the operation public static Result success() { return new Result(0, "Operation successful", null); } public static Result error(String message) { return new Result(1, message, null); } }
@NoArgsConstructor: Automatically create a no-argument constructor
@AllArgsConstructor: Automatically create parameterized constructors
Requirements Analysis
For the registration function, you must first confirm whether the username is occupied, and then you can register. Secondly, since the registration form request is submitted, it is generally a POST request, while queries and other services are GET requests.
Since you want to register a user, you cannot add the password to the database intact. The data must be encrypted to ensure security. We use MD5 encryption here. You can directly use the imported tool class or choose to introduce dependencies. Here we use the MD5 tool class.
In addition, the parameters must be verified. For example, if the password is required to be 5 to 16 non-null characters, then it must be checked first to see if it meets the requirements, and then it can be added to the database.
Spring provides a parameter verification framework that uses predefined annotations to complete parameter verification, called Spring Validation.
How to use MD5 encryption and SpringValidation frameworkhttps://blog.csdn.net/m0_56308072/article/details/131101062?spm=1001.2014.3001.5501
Global exception handling
The purpose of this is because when our test fails, the response received does not conform to the result style and is not beautiful.
We prefer that even if such an error is reported, it must conform to the result class, so we can define a global class
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public Result handleException(Exception e){ e.printStackTrace(); //Some exceptions may not have a message, so you need to use the ternary operator to check first. return Result.error(StringUtils .hasLength(e.getMessage()) ? e.getMessage() : "Operation failed"); } }
This way you can capture global exceptions.
Code Writing
Controller layer
@Validated @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @PostMapping("/register") //Verification public Result register(@Pattern(regexp = "^\S{5,16}$") String username, @Pattern(regexp = "^\S{5,16}$") String password) { //Query user User user = userService.findByUserName(username); if (user == null){ //register userService.register(username,password); return Result.success(); }else { //occupy return Result.error("Username and occupied"); } } }
Service layer
@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public User findByUserName(String name) { return userMapper.findByUserName(name); } @Override public void register(String username, String password) { //encryption String md5Password = Md5Util.getMD5String(password); //Add to userMapper.register(username,md5Password); } }
Mapper layer
@Mapper public interface UserMapper { @Select("select * from user where username = #{name}") User findByUserName(String name); @Insert("insert into user(username,password,create_time,update_time)" + "values(#{username},#{md5Password},now(),now())") void register(String username, String md5Password); }
test
3. Interface Development-Login Interface
Prerequisite preparation
Since we are developing a login interface, the most common method is to use JWT tokens to log in.
What is JWT? How to usehttps://blog.csdn.net/m0_56308072/article/details/131144785?spm=1001.2014.3001.5501
Import dependencies
<!-- JWT --> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>4.4.0</version> </dependency>
Response data
Requirements analysis
Why use jwt token? Simply put, it is because we can actually skip login verification/login and directly enter/list to access data. This is certainly not safe.
Therefore, a jwt token is needed. The token can only be obtained through /login, and then when accessing /list, only the token can be accessed.
A token is a string of characters that carries business data and reduces the number of subsequent database queries.
Prevent tampering and ensure the legality and validity of information
After the user logs in, the system will automatically issue a JWT token. Then in subsequent requests, the browser needs to carry it to the server in the request header. The name of the request header is Authorization and the value is the JWT token issued when logging in. . That is, jwt is carried in the request header. When we obtain verification, we need hetHeader to get the token and then parse and verify it.
If it is detected that the user is not logged in, the http response status code is 401. Then you need a response object (HttpServletResponse), which can change the status code
First, you need to query based on the entered user name to determine whether the user exists, and then query the password for login verification.
Code writing
JwtUtil
public class JwtUtil { private static final String KEY = "wal"; //Receive business data, generate token and return public static String genToken(Map<String, Object> claims) { return JWT.create() .withClaim("claims", claims) .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12)) .sign(Algorithm.HMAC256(KEY)); } //Receive token, verify token, and return business data public static Map<String, Object> parseToken(String token) { return JWT.require(Algorithm.HMAC256(KEY)) .build() .verify(token) .getClaim("claims") .asMap(); } }
Controller layer
Log in successfully and generate token
@PostMapping("/login") public Result<String> login(@Pattern(regexp = "^\S{5,16}$") String username, @Pattern(regexp = "^\S{5,16}$") String password){ //Query user User loginUser = userService.findByUserName(username); //Determine whether the user exists if (loginUser == null){ return Result.error("Username error"); } //Determine whether the password is correct. The password in the loginUser object is ciphertext. if(Md5Util.getMD5String(password).equals(loginUser.getPassword())){ //Login successful, generate Token Map<String,Object> claims = new HashMap<>(); claims.put("id",loginUser.getId()); claims.put("username",loginUser.getUsername()); String token = JwtUtil.genToken(claims); return Result.success(token); } return Result.error("Wrong password"); }
Access other interfaces and verify token
@RestController @RequestMapping("/article") public class ArticleController { @GetMapping("/list") public Result<String> list(@RequestHeader(name = "Authorization") String token, HttpServletResponse response){ //verify token try { Map<String, Object> claim = JwtUtil.parseToken(token); return Result.success("Access successful"); } catch (Exception e) { //not logged in response.setStatus(401); throw new RuntimeException(e); } } }
Test
Try to skip login verification and access the data interface directly
Log in normally and generate a key
Carrying a key to access the data interface
Note: If you find it too troublesome to copy and paste the returned token every time for testing, you can directly set a global token here, so that you don’t have to paste the token in the header every time you test the interface. Of course, remember to save the tab after making the change, otherwise it will not take effect.
But please note that after configuring the complete office token here, do not carry the token separately. In this case, you will carry two tokens and report an error.
com.auth0.jwt.exceptions.JWTDecodeException: The token was expected to have 3 parts, but got > 3.
Interceptor
In the above example, it cannot be reflected when we have only one data access interface, but if we have written many interfaces now, it would be too cumbersome to write the logic of verification token in each interface. We hope that this kind of highly reusable code can only be written once, and then we can add functions without disturbing the original code, which is the idea of AOP. The technology applied here is the interceptor.
To use an interceptor, first create an interceptor class and then integrate the HandlerInterceptor interface. The most important thing is the preHandle method
preHandle method: executed before the target method is executed, that is, it is intercepted directly when you access the interface. Then after intercepting it, you need to verify the token.
We used to get it directly through parameter declaration, but it is not available in this overridden method. This is because it is directly included in the parameter request (HttpServletRequest). As the name suggests, this object represents the request, and all request data is in the request object; on the contrary, the parameter response (HttpServletRequest) represents the response, and all The response data is all in response data.
@Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //Get token String token = request.getHeader("Authorization"); //verify token try { Map<String,Object> claims = JwtUtil.parseToken(token); //If there is no exception, let it go return true; } catch (Exception e) { //Not logged in, not allowed response.setStatus(401); return false; } } }
Although the interceptor is completed at this time, it has not yet taken effect. You need to register it before it will take effect to ensure safety. Adding the @Component annotation to the interceptor means that it is handed over to the IOC container management and becomes a bean object.
Then we create an interceptor’s config in config to indicate which interceptors are enabled, and rewrite the addIntercepter method in it. As the name suggests, just add those interceptors.
@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { //Release the login interface and registration interface registry.addInterceptor(loginInterceptor) .excludePathPatterns("/user/login","/user/register"); } }
Note: Since it is an interceptor, it can intercept all interfaces. Be careful not to let it intercept the login interface and registration interface. Otherwise, you need to log in to the generated token to access the data interface. To log in, you need to access the login interface. Since the login interface is also intercepted, you need to log in to access the login interface. The generated token, to log in with the token, you need to access the login interface… Infinite matryoshka dolls are turned on. addInterceptor is to add the interceptor to be used, and excludePathPatterns is to exclude the intercepted interface.
Test
When we do not carry the token, there is no response and the status code is 401
Successfully accessed while carrying the token
The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. Cloud native entry-level skills treeHomepageOverview 17034 people are learning the system