Session登陆方案的优缺点:
- 优点:Servlet容器自带,使用方便,性能高效
- 缺点:协议变成有状态,无法实现集群下的 session 共享
Token + Redis 登陆方案:
登陆流程:
- 登陆接口接收前端传入的用户名密码参数
- 验证用户名和密码是否正确
- 基于当前用户信息,生成JWT令牌
- 返回 JWT 令牌给前端
登陆拦截器:
- 从请求中获取到 JWT 令牌
- 利用 JWT 的 SDK 对令牌进行解析,判断是否能够通过校验
- 只要令牌解析可以通过,就代表令牌是有效的,用户是登陆过的。
Token 续期:
- 使用 access_token 和 refresh_token,其中 refresh_token 的过期时间是 access_token 的两倍,当 access_token 过期后使用 refresh_token 重新获取 access_token 和 refresh_token,如果 refresh_token 过期则重新登陆。
- 使用 Redis + JWT
登陆拦截
该部分内容见:安全模块
引入安全模块的依赖
<dependency> <groupId>com.swx</groupId> <artifactId>trip-common-security</artifactId> </dependency>
|
接口信息
返回VO
使用返回VO而非完整用户对象,可以避免返回敏感信息,被分析出表结构。
在 trip-users-api 模块中新建包 com.swx.user.vo,在该包下定义 LoginUserVo 类,用于接收请求参数
LoginUserVo@Getter @Setter public class LoginUserVo {
private Long id; private String nickname; private String phone; private String email; private Integer gender; private String city; private String headImgUrl; private String info; }
|
定义Service
找到 UserInfoService,定义登陆方法:
UserInfoService
Map<String, Object> login(String username, String password);
|
在 UserServiceImpl 中实现该方法:
UserServiceImpl@Service public class UserServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
private final TokenService tokenService;
public UserServiceImpl(TokenService tokenService) { this.tokenService = tokenService; }
@Override public Map<String, Object> login(String username, String password) { UserInfo userInfo = this.findByPhone(username); if (userInfo == null) { throw new BizException(500401, "用户名或密码错误"); } String encryptPassword = Md5Utils.getMD5(password + username); if (!encryptPassword.equalsIgnoreCase(userInfo.getPassword())) { throw new BizException(500401, "用户名或密码错误"); }
LoginUser loginUser = new LoginUser(); BeanUtils.copyProperties(userInfo, loginUser); String jwtToken = tokenService.createToken(loginUser);
Map<String, Object> data = new HashMap<>(); LoginUserVo loginUserVo = new LoginUserVo(); BeanUtils.copyProperties(userInfo, loginUserVo); data.put("token", jwtToken); data.put("user", loginUserVo); return data; } }
|
定义Controller
在 UserInfoController 下定义登陆方法:
UserInfoController@PostMapping("/login") public R<Map<String, Object>> login(String username, String password) { Map<String, Object> map = userInfoService.login(username, password); return R.ok(map); }
|