这节课我们要动手开发用户登陆程序,只要我们参照时序图的设计,实现后端项目的登陆程序并不难。
一、编写持久层
在TbUserDao.xml
文件中添加用于登陆的SQL语句,原理并不复杂。根据登陆页面提交的用户名和密钥,到数据库中查找是否有匹配的数据。因为数据库中的密码是加密过的,所以我们要把登陆的密码加密之后,再跟数据库中的记录做比对。
<select id="login" parameterType="HashMap" resultType="Integer">
SELECT id
FROM tb_user
WHERE username = #{username}
AND password = HEX(AES_ENCRYPT(#{password}, #{username}))
LIMIT 1;
</select>
SQL语句中为什么要出现LIMIT 1
这样的语法?其实我们心里清楚,数据库中能匹配到的登陆信息最多只有一条(用户名是唯一的),所以当数据库查询到第一个符合条件的记录之后,没必要再继续查询下去了,节省查询时间。LIMIT 1
可以让数据库只查询符合条件的第一条记录。
大家请注意,SELECT子句中返回的是主键id值。如果没查询到匹配的用户记录,数据库会返回null值,所以resultType属性值必须是Integer,而不能是int,因为int无法保存null值。
在TbUserDao.java
接口中定义抽象的Dao方法,与SQL语句对应。
@Mapper
public interface TbUserDao {
……
public Integer login(HashMap param);
}
二、编写业务层
在UserService.java
接口中声明抽象的的登陆方法。
public interface UserService {
……
public Integer login(HashMap param);
}
在UserServiceImpl.java
中实现登陆方法,就是把查询到用户ID返回。
@Service
public class UserServiceImpl implements UserService {
……
@Override
public Integer login(HashMap param) {
Integer userId = userDao.login(param);
return userId;
}
}
三、编写Web层
我们首先需要创建一个Form类保存HTTP请求提交的数据,使用Form类的好处是可以为变量设置注解,自动完成后端验证。在com.example.emos.api.controller.form
包中创建LoginForm.java
类。
@Data
@Schema(description = "登陆表单类")
public class LoginForm {
@NotBlank(message = "username不能为空")
@Pattern(regexp = "^[a-zA-Z0-9]{5,20}$", message = "username内容不正确")
@Schema(description = "用户名")
private String username;
@NotBlank(message = "password不能为空")
@Pattern(regexp = "^[a-zA-Z0-9]{6,20}$", message = "password内容不正确")
@Schema(description = "密码")
private String password;
}
在UserController.java
类中定义Web方法用于处理登陆请求。
@RestController
@RequestMapping("/user")
@Tag(name = "UserController", description = "用户Web接口")
public class UserController {
……
@PostMapping("/login")
@Operation(summary = "登陆系统")
public R login(@Valid @RequestBody LoginForm form) {
HashMap param = JSONUtil.parse(form).toBean(HashMap.class);
Integer userId = userService.login(param);
R r = R.ok().put("result", userId != null ? true : false);
if (userId != null) {
StpUtil.setLoginId(userId);
Set<String> permissions = userService.searchUserPermissions(userId);
/*
* 因为新版的Chrome浏览器不支持前端Ajax的withCredentials,
* 导致Ajax无法提交Cookie,所以我们要取出生成的Token返回给前端,
* 让前端保存在Storage中,然后每次在Ajax的Header上提交Token
*/
String token=StpUtil.getTokenInfo().getTokenValue();
r.put("permissions",permissions).put("token",token);
}
return r;
}
}
用Swagger测试的时候,打开浏览器输入http://localhost:8090/emos-api/swagger-ui.html
,然后找到Web方法执行测试,这种做法做简单。
如果你访问的是http://localhost:8090/emos-api/swagger-ui/index.html
,则需要在页面上设置参数,才能看到要测试的Web方法。
Sa-Token框架要求我们用户登陆成功之后,必须在Web方法中执行StpUtil.setLoginId(userId)
,Sa-Token框架会根据UserId自动生成Token令牌,缓存到Redis里,然后把Token以Cookie的形式存储到浏览器上面。
浏览器再提交什么请求,都会带上Cookie(含有Token),这样Sa-Token拦截请求,检查Cookie是否含有合法的Token(与Redis缓存的Token比对),就能判断出用户是否登陆了系统。如果没登陆系统,就跳转到登陆画面。