table of Contents
User login
In the previous article we implemented user registration and authentication, then we continue to achieve its login, and after a successful login information to be displayed on the page.
Next, we write code.
Achieve service
In the com.liferunner.service.IUserService
Add user login interface methods:
public interface IUserService {
...
/**
* 用户登录
* @param userRequestDTO 请求dto
* @return 登录用户信息
* @throws Exception
*/
Users userLogin(UserRequestDTO userRequestDTO) throws Exception;
}
Then, in the com.liferunner.service.impl.UserServiceImpl
implementation class implementation:
@Service
@Slf4j
public class UserServiceImpl implements IUserService {
...
@Override
public Users userLogin(UserRequestDTO userRequestDTO) throws Exception {
log.info("======用户登录请求:{}", userRequestDTO);
Example example = new Example(Users.class);
val condition = example.createCriteria();
condition.andEqualTo("username", userRequestDTO.getUsername());
condition.andEqualTo("password", MD5GeneratorTools.getMD5Str(userRequestDTO.getPassword()));
val user = this.usersMapper.selectOneByExample(example);
log.info("======用户登录处理结果:{}", user);
return user;
}
}
Error Tips:
Here's a little 坑点
, we must pay attention, use selectOneByExample()
the query when the process the incoming parameters must be noted that tk.mybatis.mapper.entity.Example
instance, rather than tk.mybatis.mapper.entity.Example.Criteria
otherwise, will be reported dynamically generated SQL query error information is as follows:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'distinct' in 'class tk.mybatis.mapper.entity.Example$Criteria'
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:92)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:440)
at com.sun.proxy.$Proxy106.selectOne(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:159)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:87)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:93)
at com.sun.proxy.$Proxy109.selectOneByExample(Unknown Source)
at com.liferunner.service.impl.UserServiceImpl.userLogin(UserServiceImpl.java:80)
...
When writing new code, it is particularly vulnerable on one line written query variables, the next line directly with the open, the more people the more simple error can not start.
Realization Controller
@RestController
@RequestMapping(value = "/users")
@Slf4j
@Api(tags = "用户管理")
public class UserController {
...
@ApiOperation(value = "用户登录", notes = "用户登录接口")
@PostMapping("/login")
public JsonResponse userLogin(@RequestBody UserRequestDTO userRequestDTO,
HttpServletRequest request,
HttpServletResponse response) {
try {
if (StringUtils.isBlank(userRequestDTO.getUsername()))
return JsonResponse.errorMsg("用户名不能为空");
if (StringUtils.isBlank(userRequestDTO.getPassword()) ||
userRequestDTO.getPassword().length() < 8) {
return JsonResponse.errorMsg("密码为空或长度小于8位");
}
val user = this.userService.userLogin(userRequestDTO);
UserResponseDTO userResponseDTO = new UserResponseDTO();
BeanUtils.copyProperties(user, userResponseDTO);
log.info("BeanUtils copy object {}", userResponseDTO);
if (null != userResponseDTO) {
// 设置前端存储的cookie信息
CookieTools.setCookie(request, response, "user",
JSON.toJSONString(userResponseDTO), true);
return JsonResponse.ok(userResponseDTO);
}
} catch (Exception e) {
e.printStackTrace();
log.error("用户登录失败,{},exception = {}", userRequestDTO, e.getMessage());
}
return JsonResponse.errorMsg("用户登录失败");
}
}
In the above code, the basic calibration problem will not go into, we focus on a few new features information:
com.liferunner.dto.UserResponseDTO
We will need to show it to the front of the data package for a new return object, we query from the database ofUsers
pojo contains all the user data, such as wherepassword
,mobile
and so some users private data is not supposed to show it to the front, even to show that also need to go through desensitization, and encryption. Thus, common practice is to package a new object is returned, which needs to contain only the front end of the data field needs it.
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel(value = "用户信息返回DTO", description = "用户登录成功后需要的返回对象")
public class UserResponseDTO {
/**
* 主键id
*/
private String id;
/**
* 用户名
*/
private String username;
/**
* 昵称 昵称
*/
private String nickname;
/**
* 头像
*/
private String face;
/**
* 性别 1:男 0:女 2:保密
*/
private Integer sex;
}
Here suggest that you use Ctrl+C
our com.liferunner.pojo.Users
object, and then delete the fields we do not need it, why do 建议
it, it is because a good do.
org.springframework.beans.BeanUtils.copyProperties(user, userResponseDTO);
As you can see, here is the direct use ofSpring BeanUtils
the value of the copy tool classes, it reduces us to loop through each field one by one to the assignment(SetValue)
of work. (Also a lazy little trick Oh, this is not right -)CookieTools.setCookie();
We have mentioned before, under normal circumstances, after the user logs on us, data will be stored in the local browserCookie
, such as my loginbaidu.com
:
At this point, the mouse in the picture on the left side of theCookies => www.baidu.com
rightclear
, then refresh our current interface again, the effect as follows:
we can see from the login state has become the exit status, andCookies
the content is also a lot less, which shows that Baidu is to our users after login information encrypted and stored in a browser cookie.
We can see Jingdong, Taobao, etc., is also based on achieved in this way, at the beginning of the opening once said, our system is based on the demo production to achieve, then we do with mainstream implementation. Of course, some students will say, that we should pass data to the front end, so that the front end to achieve! ! ! Of course, you're right, but we have one implementation, for us personally should not hurt, right?
Here you need a utility class, we can at github portal to download code. Directorycom.liferunner.utils.CookieTools
.com.alibaba.fastjson.JSON.toJSONString(userResponseDTO)
Because we want the return of an object, butcookie
we need to put thatString
here we introduced alibaba's JSON tool,mscx-shop-common/pom.xml
adding it depends on:<dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.56</version> </dependency> </dependencies>
The user logs out
After the end user, we need to be user logs out from the system, because our users login information will be stored in a browser cookie, so we need to delete the associated user cache based on user logout operation:
@ApiOperation(value = "用户登出",notes = "用户登出",httpMethod = "POST")
@PostMapping("/logout")
public JsonResponse userLogout(@RequestParam String uid,
HttpServletRequest request,HttpServletResponse response){
// clear front's user cookies
CookieTools.deleteCookie(request,response,"user");
// return operational result
return JsonResponse.ok();
}
Development and debugging small welfare
java log tracking
General electric business scenarios, the response time of the request has an extremely stringent requirements, such as when you buy a commodity at a site, if every click of a button should wait, or the system Caton feel it, you will not hesitate to select the upper right corner 小红叉
, get rid of it. Therefore, in the development of our system, often we need to monitor the response time of our request, even to test stress tests. But let us do every method to achieve such a request is clearly unreasonable and even that is to allow developers uncomfortable, so we have to achieve a common practice, and that is through AOP
, aspect-oriented to achieve. Section on basic use, we can refer to the AOP Portal , Next, we start coding.
According to springboot
realize the function Trilogy:
SETP 1. Add dependent
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
step 2. startup configuration (not just ignore this step)
SETP 3. annotated
in our mscx-shop-api
project, create a com.liferunner.api.aspect
package, and then create the com.liferunner.api.aspect.CommonLogAspect
following code:
package com.liferunner.api.aspect;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* CommonLogAspect for : AOP切面实现日志确认
*
* @author <a href="mailto:[email protected]">Isaac.Zhang | 若初</a>
* @since 2019/11/11
*/
@Component
@Aspect
@Slf4j
public class CommonLogAspect {
@Around("execution(* com.liferunner.api.controller..*.*(..))")
public void recordLogTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
log.info("----------- {}.{} process log time started.---------------",
proceedingJoinPoint.getTarget().getClass(),
proceedingJoinPoint.getSignature().getName());
val startTime = System.currentTimeMillis();
proceedingJoinPoint.proceed();
val afterTime = System.currentTimeMillis();
if (afterTime - startTime > 1000) {
log.warn("cost : {}", afterTime - startTime);
} else {
log.info("cost : {}", afterTime - startTime);
}
log.info("----------- {}.{} process log time ended.---------------",
proceedingJoinPoint.getSourceLocation().getClass(),
proceedingJoinPoint.getSignature().getName());
}
}
- The first line of the log on our behalf which method you want to monitor what kind of
proceedingJoinPoint.proceed();
Representation execution- When the request is checked 1000ms, we use
log.warn(...)
a log warning
step 4. The demo
- Users consuming queries
- Registered users consuming
From the chart, we can clearly see that every time we request time-consuming, and then it can be targeted to optimize every method! ! !
sql log tracking
In the process of our development, often run against the database of the CRUD
operation, however, because we used mybatis
to dynamically generate a simple SQL query, rather than manually writing, such as we are in UserServiceImpl.java
the user's query and implement user registration code the tk.mybatis.mapper.entity.Example
wellthis.usersMapper.insertSelective(user);
public Users findUserByUserName(String username) {
// 构建查询条件
Example example = new Example(Users.class);
val condition = example.createCriteria()
.andEqualTo("username", username);
return this.usersMapper.selectOneByExample(example);
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public Users createUser(UserRequestDTO userRequestDTO) throws Exception {
log.info("======begin create user : {}=======", userRequestDTO);
val user = Users.builder()
.id(sid.next()) //生成分布式id
.username(userRequestDTO.getUsername())
.password(MD5GeneratorTools.getMD5Str(userRequestDTO.getPassword()))
.birthday(DateUtils.parseDate("1970-01-01", "yyyy-MM-dd"))
.nickname(userRequestDTO.getUsername())
.face(this.FACE_IMG)
.sex(SexEnum.secret.type)
.createdTime(new Date())
.updatedTime(new Date())
.build();
this.usersMapper.insertSelective(user);
log.info("======end create user : {}=======", userRequestDTO);
return user;
}
Once having problems, we often do not know in the end is where the error occurred, this time we are SQL
there a problem we do not know, so let's configure a way for us 看
to small implementations of SQL:
1. Set logging configuration (FIG.)
2. Modification mybatis configuration ( log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
)
3. SELECT
The demo
4. INSERT
demonstration effect
from the console can be seen in FIG JDBC operation twice, the first time is in fact our username check. The second INSERT
is the real insert.
By demonstrating the above results, we can think of, this log needle solve problems in our daily development is very necessary. But we must remember that, at the time of production, the log must be closed, otherwise after a large amount of data once, the performance of the system will cause serious injury! ! !
Source download
Notice under section
The next section we will continue to develop a core part of our electricity supplier - Product and display ads, to use in the course of any development component, I will be a special presentation by one of the brothers late panic!
gogogo!