feat:github登陆接入
This commit is contained in:
parent
cc71076b45
commit
1196926ac9
@ -1,9 +1,12 @@
|
||||
package com.fanxb.bookmark.business.user.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.fanxb.bookmark.business.user.vo.LoginBody;
|
||||
import com.fanxb.bookmark.business.user.vo.RegisterBody;
|
||||
import com.fanxb.bookmark.business.user.service.OAuthService;
|
||||
import com.fanxb.bookmark.business.user.service.UserService;
|
||||
import com.fanxb.bookmark.business.user.vo.LoginBody;
|
||||
import com.fanxb.bookmark.business.user.vo.OAuthBody;
|
||||
import com.fanxb.bookmark.business.user.vo.RegisterBody;
|
||||
import com.fanxb.bookmark.business.user.service.impl.UserServiceImpl;
|
||||
import com.fanxb.bookmark.common.entity.Result;
|
||||
import com.fanxb.bookmark.common.util.UserContextHolder;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
@ -22,8 +25,16 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
@RequestMapping("/user")
|
||||
public class UserController {
|
||||
|
||||
private final UserServiceImpl userServiceImpl;
|
||||
private final OAuthService oAuthService;
|
||||
private final UserService userService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
public UserController(UserServiceImpl userServiceImpl, OAuthService oAuthService, UserService userService) {
|
||||
this.userServiceImpl = userServiceImpl;
|
||||
this.oAuthService = oAuthService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description: 获取验证码
|
||||
@ -35,7 +46,7 @@ public class UserController {
|
||||
*/
|
||||
@GetMapping("/authCode")
|
||||
public Result getAuthCode(@Param("email") String email) {
|
||||
userService.sendAuthCode(email);
|
||||
userServiceImpl.sendAuthCode(email);
|
||||
return Result.success(null);
|
||||
}
|
||||
|
||||
@ -49,7 +60,7 @@ public class UserController {
|
||||
*/
|
||||
@PutMapping("")
|
||||
public Result register(@RequestBody RegisterBody body) {
|
||||
return Result.success(userService.register(body));
|
||||
return Result.success(userServiceImpl.register(body));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -61,7 +72,7 @@ public class UserController {
|
||||
*/
|
||||
@GetMapping("/currentUserInfo")
|
||||
public Result currentUserInfo() {
|
||||
return Result.success(userService.getUserInfo(UserContextHolder.get().getUserId()));
|
||||
return Result.success(userServiceImpl.getUserInfo(UserContextHolder.get().getUserId()));
|
||||
}
|
||||
|
||||
|
||||
@ -72,7 +83,7 @@ public class UserController {
|
||||
*/
|
||||
@PostMapping("/icon")
|
||||
public Result pushIcon(@RequestParam("file") MultipartFile file) throws Exception {
|
||||
return Result.success(userService.updateIcon(file));
|
||||
return Result.success(userServiceImpl.updateIcon(file));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,7 +96,7 @@ public class UserController {
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
public Result login(@RequestBody LoginBody body) {
|
||||
return Result.success(userService.login(body));
|
||||
return Result.success(userServiceImpl.login(body));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,7 +109,7 @@ public class UserController {
|
||||
*/
|
||||
@PostMapping("/resetPassword")
|
||||
public Result resetPassword(@RequestBody RegisterBody body) {
|
||||
userService.resetPassword(body);
|
||||
userServiceImpl.resetPassword(body);
|
||||
return Result.success(null);
|
||||
}
|
||||
|
||||
@ -112,7 +123,7 @@ public class UserController {
|
||||
*/
|
||||
@PostMapping("/checkPassword")
|
||||
public Result checkPassword(@RequestBody JSONObject obj) {
|
||||
return Result.success(userService.checkPassword(obj.getString("password")));
|
||||
return Result.success(userServiceImpl.checkPassword(obj.getString("password")));
|
||||
}
|
||||
|
||||
@GetMapping("/loginStatus")
|
||||
@ -120,5 +131,28 @@ public class UserController {
|
||||
return Result.success(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 第三方登陆
|
||||
*
|
||||
* @param body 入参
|
||||
* @return com.fanxb.bookmark.common.entity.Result
|
||||
* @author fanxb
|
||||
* @date 2021/3/10
|
||||
*/
|
||||
@PostMapping("oAuthLogin")
|
||||
public Result oAuthLogin(@RequestBody OAuthBody body) {
|
||||
return Result.success(oAuthService.oAuthCheck(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户version
|
||||
*
|
||||
* @date 2021/3/11
|
||||
**/
|
||||
@GetMapping("/version")
|
||||
public Result getUserVersion() {
|
||||
return Result.success(userService.getCurrentUserVersion(UserContextHolder.get().getUserId()));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package com.fanxb.bookmark.business.user.dao;
|
||||
import com.fanxb.bookmark.common.entity.User;
|
||||
import com.fanxb.bookmark.common.entity.redis.UserBookmarkUpdate;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@ -64,7 +65,7 @@ public interface UserDao {
|
||||
* @author fanxb
|
||||
* @date 2019/7/30 16:08
|
||||
*/
|
||||
User selectByUserId(int userId);
|
||||
User selectByUserIdOrGithubId(@Param("userId") Integer userId, @Param("githubId") Long githubId);
|
||||
|
||||
/**
|
||||
* Description: 更新用户icon
|
||||
@ -102,11 +103,11 @@ public interface UserDao {
|
||||
/**
|
||||
* 更新用户新邮箱
|
||||
*
|
||||
* @param userId userId
|
||||
* @param newPassword userId
|
||||
* @param userId userId
|
||||
* @param newEmail email
|
||||
*/
|
||||
@Update("update user set newEmail=#{newPassword} where userId= #{userId}")
|
||||
void updateNewEmailByUserId(@Param("userId") int userId, @Param("newPassword") String newPassword);
|
||||
@Update("update user set newEmail=#{newEmail} where userId= #{userId}")
|
||||
void updateNewEmailByUserId(@Param("userId") int userId, @Param("newEmail") String newEmail);
|
||||
|
||||
/**
|
||||
* 新邮箱校验成功,更新邮箱
|
||||
@ -135,4 +136,36 @@ public interface UserDao {
|
||||
*/
|
||||
@Update("update user set version=version+1")
|
||||
void updateAllBookmarkUpdateVersion();
|
||||
|
||||
/**
|
||||
* 判断用户名是否存在
|
||||
*
|
||||
* @param name name
|
||||
* @return boolean
|
||||
* @author fanxb
|
||||
* @date 2021/3/11
|
||||
**/
|
||||
@Select("select count(1) from user where username=#{name}")
|
||||
boolean usernameExist(String name);
|
||||
|
||||
/**
|
||||
* 更新githubId
|
||||
*
|
||||
* @param user user
|
||||
* @author fanxb
|
||||
* @date 2021/3/11
|
||||
**/
|
||||
@Update("update user set githubId=#{githubId},email=#{email} where userId=#{userId}")
|
||||
void updateEmailAndGithubId(User user);
|
||||
|
||||
/**
|
||||
* 获取用户版本
|
||||
*
|
||||
* @param userId userId
|
||||
* @return int
|
||||
* @author fanxb
|
||||
* @date 2021/3/11
|
||||
**/
|
||||
@Select("select version from user where userId=#{userId}")
|
||||
int getUserVersion(int userId);
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ public class BaseInfoService {
|
||||
|
||||
public void changePassword(UpdatePasswordBody body) {
|
||||
int userId = UserContextHolder.get().getUserId();
|
||||
String password = userDao.selectByUserId(userId).getPassword();
|
||||
String password = userDao.selectByUserIdOrGithubId(userId, null).getPassword();
|
||||
if (!StrUtil.equals(password, HashUtil.getPassword(body.getOldPassword()))) {
|
||||
throw new CustomException("旧密码错误");
|
||||
}
|
||||
@ -66,7 +66,7 @@ public class BaseInfoService {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateEmail(EmailUpdateBody body) {
|
||||
int userId = UserContextHolder.get().getUserId();
|
||||
String oldPassword = userDao.selectByUserId(userId).getPassword();
|
||||
String oldPassword = userDao.selectByUserIdOrGithubId(userId, null).getPassword();
|
||||
if (!StrUtil.equals(oldPassword, HashUtil.getPassword(body.getOldPassword()))) {
|
||||
throw new CustomException("密码校验失败,无法更新email");
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
package com.fanxb.bookmark.business.user.service;
|
||||
|
||||
public class OAuthService {
|
||||
import com.fanxb.bookmark.business.user.vo.OAuthBody;
|
||||
|
||||
public interface OAuthService {
|
||||
|
||||
String oAuthCheck(OAuthBody body);
|
||||
}
|
||||
|
@ -1,219 +1,43 @@
|
||||
package com.fanxb.bookmark.business.user.service;
|
||||
|
||||
import com.fanxb.bookmark.business.user.constant.FileConstant;
|
||||
import com.fanxb.bookmark.business.user.dao.UserDao;
|
||||
import com.fanxb.bookmark.business.user.vo.LoginBody;
|
||||
import com.fanxb.bookmark.business.user.vo.LoginRes;
|
||||
import com.fanxb.bookmark.business.user.vo.RegisterBody;
|
||||
import com.fanxb.bookmark.common.constant.Constant;
|
||||
import com.fanxb.bookmark.common.constant.NumberConstant;
|
||||
import com.fanxb.bookmark.common.constant.RedisConstant;
|
||||
import com.fanxb.bookmark.common.entity.MailInfo;
|
||||
import com.fanxb.bookmark.common.entity.User;
|
||||
import com.fanxb.bookmark.common.exception.FormDataException;
|
||||
import com.fanxb.bookmark.common.util.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 类功能简述:
|
||||
* 类功能详述:
|
||||
*
|
||||
* @author fanxb
|
||||
* @date 2019/7/5 17:39
|
||||
*/
|
||||
@Service
|
||||
public class UserService {
|
||||
|
||||
private static final String DEFAULT_ICON = "/favicon.ico";
|
||||
/**
|
||||
* 短期jwt失效时间
|
||||
*/
|
||||
private static final long SHORT_EXPIRE_TIME = 2 * 60 * 60 * 1000;
|
||||
/**
|
||||
* 长期jwt失效时间
|
||||
*/
|
||||
private static final long LONG_EXPIRE_TIME = 30L * TimeUtil.DAY_MS;
|
||||
|
||||
/**
|
||||
* 头像文件大小限制 单位:KB
|
||||
*/
|
||||
private static final int ICON_SIZE = 200;
|
||||
|
||||
@Autowired
|
||||
private UserDao userDao;
|
||||
@Autowired
|
||||
private StringRedisTemplate redisTemplate;
|
||||
|
||||
/**
|
||||
* Description: 向目标发送验证码
|
||||
*
|
||||
* @param email 目标
|
||||
* @author fanxb
|
||||
* @date 2019/7/5 17:48
|
||||
*/
|
||||
public void sendAuthCode(String email) {
|
||||
MailInfo info = new MailInfo();
|
||||
info.setSubject("签签世界注册验证码");
|
||||
String code = StringUtil.getRandomString(6, 2);
|
||||
info.setContent("欢迎注册 签签世界 ,本次验证码");
|
||||
info.setContent(code + " 是您的验证码,注意验证码有效期为15分钟哦!");
|
||||
info.setReceiver(email);
|
||||
if (Constant.isDev) {
|
||||
code = "123456";
|
||||
} else {
|
||||
MailUtil.sendTextMail(info);
|
||||
}
|
||||
RedisUtil.set(Constant.authCodeKey(email), code, Constant.AUTH_CODE_EXPIRE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description: 用户注册
|
||||
*
|
||||
* @param body 注册表单
|
||||
* @author fanxb
|
||||
* @date 2019/7/6 11:30
|
||||
*/
|
||||
public LoginRes register(RegisterBody body) {
|
||||
User user = userDao.selectByUsernameOrEmail(body.getUsername(), body.getEmail());
|
||||
if (user != null) {
|
||||
if (user.getUsername().equals(body.getUsername())) {
|
||||
throw new FormDataException("用户名已经被注册");
|
||||
}
|
||||
if (user.getEmail().equals(body.getEmail())) {
|
||||
throw new FormDataException("邮箱已经被注册");
|
||||
}
|
||||
}
|
||||
user = new User();
|
||||
user.setUsername(body.getUsername());
|
||||
user.setEmail(body.getEmail());
|
||||
user.setIcon(DEFAULT_ICON);
|
||||
user.setPassword(HashUtil.sha1(HashUtil.md5(body.getPassword())));
|
||||
user.setCreateTime(System.currentTimeMillis());
|
||||
user.setLastLoginTime(System.currentTimeMillis());
|
||||
user.setVersion(0);
|
||||
userDao.addOne(user);
|
||||
Map<String, String> data = new HashMap<>(1);
|
||||
data.put("userId", String.valueOf(user.getUserId()));
|
||||
String token = JwtUtil.encode(data, Constant.jwtSecret, LONG_EXPIRE_TIME);
|
||||
return new LoginRes(user, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description: 登录
|
||||
*
|
||||
* @param body 登录表单
|
||||
* @return LoginRes
|
||||
* @author fanxb
|
||||
* @date 2019/7/6 16:37
|
||||
*/
|
||||
public LoginRes login(LoginBody body) {
|
||||
String key = RedisConstant.getUserFailCountKey(body.getStr());
|
||||
String count = redisTemplate.opsForValue().get(key);
|
||||
if (count != null && Integer.parseInt(count) >= 5) {
|
||||
redisTemplate.expire(key, 30, TimeUnit.MINUTES);
|
||||
throw new FormDataException("您已连续输错密码5次,请30分钟后再试,或联系管理员处理");
|
||||
}
|
||||
User userInfo = userDao.selectByUsernameOrEmail(body.getStr(), body.getStr());
|
||||
if (userInfo == null || !HashUtil.sha1(HashUtil.md5(body.getPassword())).equals(userInfo.getPassword())) {
|
||||
redisTemplate.opsForValue().set(key, count == null ? "1" : String.valueOf(Integer.parseInt(count) + 1), 30, TimeUnit.MINUTES);
|
||||
throw new FormDataException("账号密码错误");
|
||||
}
|
||||
redisTemplate.delete(key);
|
||||
Map<String, String> data = new HashMap<>(1);
|
||||
data.put("userId", String.valueOf(userInfo.getUserId()));
|
||||
String token = JwtUtil.encode(data, Constant.jwtSecret, body.isRememberMe() ? LONG_EXPIRE_TIME : SHORT_EXPIRE_TIME);
|
||||
LoginRes res = new LoginRes();
|
||||
res.setToken(token);
|
||||
res.setUser(userInfo);
|
||||
userDao.updateLastLoginTime(System.currentTimeMillis(), userInfo.getUserId());
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description: 重置密码
|
||||
*
|
||||
* @param body 重置密码 由于参数和注册差不多,所以用同一个表单
|
||||
* @author fanxb
|
||||
* @date 2019/7/9 19:59
|
||||
*/
|
||||
public void resetPassword(RegisterBody body) {
|
||||
User user = userDao.selectByUsernameOrEmail(body.getEmail(), body.getEmail());
|
||||
if (user == null) {
|
||||
throw new FormDataException("用户不存在");
|
||||
}
|
||||
String codeKey = Constant.authCodeKey(body.getEmail());
|
||||
String realCode = RedisUtil.get(codeKey, String.class);
|
||||
if (StringUtil.isEmpty(realCode) || (!realCode.equals(body.getAuthCode()))) {
|
||||
throw new FormDataException("验证码错误");
|
||||
}
|
||||
RedisUtil.delete(codeKey);
|
||||
String newPassword = HashUtil.getPassword(body.getPassword());
|
||||
userDao.resetPassword(newPassword, body.getEmail());
|
||||
}
|
||||
|
||||
/**
|
||||
* Description: 根据userId获取用户信息
|
||||
*
|
||||
* @param userId userId
|
||||
* @return com.fanxb.bookmark.common.entity.User
|
||||
* @author fanxb
|
||||
* @date 2019/7/30 15:57
|
||||
*/
|
||||
public User getUserInfo(int userId) {
|
||||
return userDao.selectByUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户头像
|
||||
*
|
||||
* @param file file
|
||||
* @return 访问路径
|
||||
*/
|
||||
public String updateIcon(MultipartFile file) throws Exception {
|
||||
if (file.getSize() / NumberConstant.K_SIZE > ICON_SIZE) {
|
||||
throw new FormDataException("文件大小超过限制");
|
||||
}
|
||||
int userId = UserContextHolder.get().getUserId();
|
||||
String fileName = file.getOriginalFilename();
|
||||
assert fileName != null;
|
||||
String path = Paths.get(FileConstant.iconPath, userId + "." + System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf("."))).toString();
|
||||
Path realPath = Paths.get(Constant.fileSavePath, path);
|
||||
FileUtil.ensurePathExist(realPath.getParent().toString());
|
||||
file.transferTo(realPath);
|
||||
path = File.separator + path;
|
||||
userDao.updateUserIcon(userId, path);
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能描述: 密码校验,校验成功返回一个actionId,以执行敏感操作
|
||||
*
|
||||
* @param password password
|
||||
* @return java.lang.String
|
||||
* @author fanxb
|
||||
* @date 2019/11/11 23:41
|
||||
*/
|
||||
public String checkPassword(String password) {
|
||||
int userId = UserContextHolder.get().getUserId();
|
||||
String pass = HashUtil.getPassword(password);
|
||||
User user = userDao.selectByUserId(userId);
|
||||
if (!user.getPassword().equals(pass)) {
|
||||
throw new FormDataException("密码错误,请重试");
|
||||
}
|
||||
String actionId = UUID.randomUUID().toString().replaceAll("-", "");
|
||||
String key = RedisConstant.getPasswordCheckKey(userId, actionId);
|
||||
RedisUtil.set(key, "1", 5 * 60 * 1000);
|
||||
return actionId;
|
||||
}
|
||||
}
|
||||
package com.fanxb.bookmark.business.user.service;
|
||||
|
||||
import com.fanxb.bookmark.common.util.TimeUtil;
|
||||
|
||||
/**
|
||||
* 用户接口
|
||||
*
|
||||
* @author fanxb
|
||||
* @date 2021/3/11
|
||||
**/
|
||||
public interface UserService {
|
||||
static final String DEFAULT_ICON = "/favicon.ico";
|
||||
/**
|
||||
* 短期jwt失效时间
|
||||
*/
|
||||
static final long SHORT_EXPIRE_TIME = 2 * 60 * 60 * 1000;
|
||||
/**
|
||||
* 长期jwt失效时间
|
||||
*/
|
||||
static final long LONG_EXPIRE_TIME = 30L * TimeUtil.DAY_MS;
|
||||
|
||||
/**
|
||||
* 头像文件大小限制 单位:KB
|
||||
*/
|
||||
static final int ICON_SIZE = 200;
|
||||
|
||||
/***
|
||||
* 获取一个可用的用户名
|
||||
* @author fanxb
|
||||
* @return java.lang.String
|
||||
* @date 2021/3/11
|
||||
**/
|
||||
String createNewUsername();
|
||||
|
||||
/**
|
||||
* 获取当前用户的version
|
||||
*
|
||||
* @return int
|
||||
* @author fanxb
|
||||
* @date 2021/3/11
|
||||
**/
|
||||
int getCurrentUserVersion(int userId);
|
||||
}
|
||||
|
@ -0,0 +1,116 @@
|
||||
package com.fanxb.bookmark.business.user.service.impl;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.fanxb.bookmark.business.user.dao.UserDao;
|
||||
import com.fanxb.bookmark.business.user.service.OAuthService;
|
||||
import com.fanxb.bookmark.business.user.service.UserService;
|
||||
import com.fanxb.bookmark.business.user.vo.OAuthBody;
|
||||
import com.fanxb.bookmark.common.constant.Constant;
|
||||
import com.fanxb.bookmark.common.entity.User;
|
||||
import com.fanxb.bookmark.common.exception.CustomException;
|
||||
import com.fanxb.bookmark.common.util.HttpUtil;
|
||||
import com.fanxb.bookmark.common.util.JwtUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.fanxb.bookmark.business.user.service.UserService.LONG_EXPIRE_TIME;
|
||||
import static com.fanxb.bookmark.business.user.service.UserService.SHORT_EXPIRE_TIME;
|
||||
|
||||
/**
|
||||
* OAuth交互类
|
||||
*
|
||||
* @author fanxb
|
||||
* @date 2021/3/10
|
||||
**/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OAuthServiceImpl implements OAuthService {
|
||||
|
||||
@Value("${OAuth.github.clientId}")
|
||||
private String githubClientId;
|
||||
@Value("${OAuth.github.secret}")
|
||||
private String githubSecret;
|
||||
private final UserDao userDao;
|
||||
private final UserService userService;
|
||||
|
||||
@Autowired
|
||||
public OAuthServiceImpl(UserDao userDao, UserService userService) {
|
||||
this.userDao = userDao;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String oAuthCheck(OAuthBody body) {
|
||||
User current, other = new User();
|
||||
if (StrUtil.equals(body.getType(), OAuthBody.GITHUB)) {
|
||||
Map<String, String> header = new HashMap<>(2);
|
||||
header.put("accept", "application/json");
|
||||
String url = "https://github.com/login/oauth/access_token?client_id=" + githubClientId + "&client_secret=" + githubSecret + "&code=" + body.getCode();
|
||||
JSONObject obj = HttpUtil.get(url, header);
|
||||
String accessToken = obj.getString("access_token");
|
||||
if (StrUtil.isEmpty(accessToken)) {
|
||||
throw new CustomException("github登陆失败,请稍后重试");
|
||||
}
|
||||
header.put("Authorization", "token " + accessToken);
|
||||
JSONObject userInfo = HttpUtil.get("https://api.github.com/user", header);
|
||||
other.setGithubId(userInfo.getLong("id"));
|
||||
if (other.getGithubId() == null) {
|
||||
log.error("github返回异常:{}", userInfo.toString());
|
||||
throw new CustomException("登陆异常,请稍后重试");
|
||||
}
|
||||
other.setEmail(userInfo.getString("email"));
|
||||
other.setIcon(userInfo.getString("avatar_url"));
|
||||
other.setUsername(userInfo.getString("login"));
|
||||
current = userDao.selectByUserIdOrGithubId(null, other.getGithubId());
|
||||
if (current == null) {
|
||||
current = userDao.selectByUsernameOrEmail(null, other.getEmail());
|
||||
}
|
||||
} else {
|
||||
throw new CustomException("不支持的登陆方式" + body.getType());
|
||||
}
|
||||
User newest = dealOAuth(current, other);
|
||||
return JwtUtil.encode(Collections.singletonMap("userId", String.valueOf(newest.getUserId())), Constant.jwtSecret
|
||||
, body.isRememberMe() ? LONG_EXPIRE_TIME : SHORT_EXPIRE_TIME);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO 方法描述
|
||||
*
|
||||
* @param current 当前是否存在该用户
|
||||
* @param other 第三方获取的数据
|
||||
* @return User 最新的用户信息
|
||||
* @author fanxb
|
||||
* @date 2021/3/11
|
||||
**/
|
||||
private User dealOAuth(User current, User other) {
|
||||
if (current == null) {
|
||||
//判断用户名是否可用
|
||||
if (userDao.usernameExist(other.getUsername())) {
|
||||
other.setUsername(userService.createNewUsername());
|
||||
}
|
||||
other.setPassword("");
|
||||
other.setCreateTime(System.currentTimeMillis());
|
||||
other.setLastLoginTime(System.currentTimeMillis());
|
||||
other.setVersion(0);
|
||||
userDao.addOne(other);
|
||||
return other;
|
||||
} else {
|
||||
if (!current.getEmail().equals(other.getEmail()) || !current.getGithubId().equals(other.getGithubId())) {
|
||||
current.setEmail(other.getEmail());
|
||||
current.setGithubId(other.getGithubId());
|
||||
userDao.updateEmailAndGithubId(current);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,219 @@
|
||||
package com.fanxb.bookmark.business.user.service.impl;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.fanxb.bookmark.business.user.constant.FileConstant;
|
||||
import com.fanxb.bookmark.business.user.dao.UserDao;
|
||||
import com.fanxb.bookmark.business.user.service.UserService;
|
||||
import com.fanxb.bookmark.business.user.vo.LoginBody;
|
||||
import com.fanxb.bookmark.business.user.vo.LoginRes;
|
||||
import com.fanxb.bookmark.business.user.vo.RegisterBody;
|
||||
import com.fanxb.bookmark.common.constant.Constant;
|
||||
import com.fanxb.bookmark.common.constant.NumberConstant;
|
||||
import com.fanxb.bookmark.common.constant.RedisConstant;
|
||||
import com.fanxb.bookmark.common.entity.MailInfo;
|
||||
import com.fanxb.bookmark.common.entity.User;
|
||||
import com.fanxb.bookmark.common.exception.FormDataException;
|
||||
import com.fanxb.bookmark.common.util.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 类功能简述:
|
||||
* 类功能详述:
|
||||
*
|
||||
* @author fanxb
|
||||
* @date 2019/7/5 17:39
|
||||
*/
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
@Autowired
|
||||
private UserDao userDao;
|
||||
@Autowired
|
||||
private StringRedisTemplate redisTemplate;
|
||||
|
||||
/**
|
||||
* Description: 向目标发送验证码
|
||||
*
|
||||
* @param email 目标
|
||||
* @author fanxb
|
||||
* @date 2019/7/5 17:48
|
||||
*/
|
||||
public void sendAuthCode(String email) {
|
||||
MailInfo info = new MailInfo();
|
||||
info.setSubject("签签世界注册验证码");
|
||||
String code = StringUtil.getRandomString(6, 2);
|
||||
info.setContent("欢迎注册 签签世界 ,本次验证码");
|
||||
info.setContent(code + " 是您的验证码,注意验证码有效期为15分钟哦!");
|
||||
info.setReceiver(email);
|
||||
if (Constant.isDev) {
|
||||
code = "123456";
|
||||
} else {
|
||||
MailUtil.sendTextMail(info);
|
||||
}
|
||||
RedisUtil.set(Constant.authCodeKey(email), code, Constant.AUTH_CODE_EXPIRE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description: 用户注册
|
||||
*
|
||||
* @param body 注册表单
|
||||
* @author fanxb
|
||||
* @date 2019/7/6 11:30
|
||||
*/
|
||||
public String register(RegisterBody body) {
|
||||
User user = userDao.selectByUsernameOrEmail(body.getUsername(), body.getEmail());
|
||||
if (user != null) {
|
||||
if (user.getUsername().equals(body.getUsername())) {
|
||||
throw new FormDataException("用户名已经被注册");
|
||||
}
|
||||
if (user.getEmail().equals(body.getEmail())) {
|
||||
throw new FormDataException("邮箱已经被注册");
|
||||
}
|
||||
}
|
||||
user = new User();
|
||||
user.setUsername(body.getUsername());
|
||||
user.setEmail(body.getEmail());
|
||||
user.setIcon(DEFAULT_ICON);
|
||||
user.setPassword(HashUtil.sha1(HashUtil.md5(body.getPassword())));
|
||||
user.setCreateTime(System.currentTimeMillis());
|
||||
user.setLastLoginTime(System.currentTimeMillis());
|
||||
user.setVersion(0);
|
||||
userDao.addOne(user);
|
||||
Map<String, String> data = new HashMap<>(1);
|
||||
data.put("userId", String.valueOf(user.getUserId()));
|
||||
return JwtUtil.encode(data, Constant.jwtSecret, LONG_EXPIRE_TIME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description: 登录
|
||||
*
|
||||
* @param body 登录表单
|
||||
* @return string
|
||||
* @author fanxb
|
||||
* @date 2019/7/6 16:37
|
||||
*/
|
||||
public String login(LoginBody body) {
|
||||
String key = RedisConstant.getUserFailCountKey(body.getStr());
|
||||
String count = redisTemplate.opsForValue().get(key);
|
||||
if (count != null && Integer.parseInt(count) >= 5) {
|
||||
redisTemplate.expire(key, 30, TimeUnit.MINUTES);
|
||||
throw new FormDataException("您已连续输错密码5次,请30分钟后再试,或联系管理员处理");
|
||||
}
|
||||
User userInfo = userDao.selectByUsernameOrEmail(body.getStr(), body.getStr());
|
||||
if (userInfo == null || StrUtil.isEmpty(userInfo.getPassword()) || !HashUtil.sha1(HashUtil.md5(body.getPassword())).equals(userInfo.getPassword())) {
|
||||
redisTemplate.opsForValue().set(key, count == null ? "1" : String.valueOf(Integer.parseInt(count) + 1), 30, TimeUnit.MINUTES);
|
||||
throw new FormDataException("账号密码错误");
|
||||
}
|
||||
redisTemplate.delete(key);
|
||||
userDao.updateLastLoginTime(System.currentTimeMillis(), userInfo.getUserId());
|
||||
return JwtUtil.encode(Collections.singletonMap("userId", String.valueOf(userInfo.getUserId())), Constant.jwtSecret
|
||||
, body.isRememberMe() ? LONG_EXPIRE_TIME : SHORT_EXPIRE_TIME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description: 重置密码
|
||||
*
|
||||
* @param body 重置密码 由于参数和注册差不多,所以用同一个表单
|
||||
* @author fanxb
|
||||
* @date 2019/7/9 19:59
|
||||
*/
|
||||
public void resetPassword(RegisterBody body) {
|
||||
User user = userDao.selectByUsernameOrEmail(body.getEmail(), body.getEmail());
|
||||
if (user == null) {
|
||||
throw new FormDataException("用户不存在");
|
||||
}
|
||||
String codeKey = Constant.authCodeKey(body.getEmail());
|
||||
String realCode = RedisUtil.get(codeKey, String.class);
|
||||
if (StringUtil.isEmpty(realCode) || (!realCode.equals(body.getAuthCode()))) {
|
||||
throw new FormDataException("验证码错误");
|
||||
}
|
||||
RedisUtil.delete(codeKey);
|
||||
String newPassword = HashUtil.getPassword(body.getPassword());
|
||||
userDao.resetPassword(newPassword, body.getEmail());
|
||||
}
|
||||
|
||||
/**
|
||||
* Description: 根据userId获取用户信息
|
||||
*
|
||||
* @param userId userId
|
||||
* @return com.fanxb.bookmark.common.entity.User
|
||||
* @author fanxb
|
||||
* @date 2019/7/30 15:57
|
||||
*/
|
||||
public User getUserInfo(int userId) {
|
||||
User user = userDao.selectByUserIdOrGithubId(userId, null);
|
||||
user.setNoPassword(StrUtil.isEmpty(user.getPassword()));
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户头像
|
||||
*
|
||||
* @param file file
|
||||
* @return 访问路径
|
||||
*/
|
||||
public String updateIcon(MultipartFile file) throws Exception {
|
||||
if (file.getSize() / NumberConstant.K_SIZE > ICON_SIZE) {
|
||||
throw new FormDataException("文件大小超过限制");
|
||||
}
|
||||
int userId = UserContextHolder.get().getUserId();
|
||||
String fileName = file.getOriginalFilename();
|
||||
assert fileName != null;
|
||||
String path = Paths.get(FileConstant.iconPath, userId + "." + System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf("."))).toString();
|
||||
Path realPath = Paths.get(Constant.fileSavePath, path);
|
||||
FileUtil.ensurePathExist(realPath.getParent().toString());
|
||||
file.transferTo(realPath);
|
||||
path = File.separator + path;
|
||||
userDao.updateUserIcon(userId, path);
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能描述: 密码校验,校验成功返回一个actionId,以执行敏感操作
|
||||
*
|
||||
* @param password password
|
||||
* @return java.lang.String
|
||||
* @author fanxb
|
||||
* @date 2019/11/11 23:41
|
||||
*/
|
||||
public String checkPassword(String password) {
|
||||
int userId = UserContextHolder.get().getUserId();
|
||||
String pass = HashUtil.getPassword(password);
|
||||
User user = userDao.selectByUserIdOrGithubId(userId, null);
|
||||
if (!user.getPassword().equals(pass)) {
|
||||
throw new FormDataException("密码错误,请重试");
|
||||
}
|
||||
String actionId = UUID.randomUUID().toString().replaceAll("-", "");
|
||||
String key = RedisConstant.getPasswordCheckKey(userId, actionId);
|
||||
RedisUtil.set(key, "1", 5 * 60 * 1000);
|
||||
return actionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createNewUsername() {
|
||||
while (true) {
|
||||
String name = RandomUtil.randomString(8);
|
||||
if (!userDao.usernameExist(name)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentUserVersion(int userId) {
|
||||
return userDao.getUserVersion(userId);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.fanxb.bookmark.business.user.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 第三方登陆入参
|
||||
*
|
||||
* @author fanxb
|
||||
* @date 2021/3/10
|
||||
**/
|
||||
@Data
|
||||
public class OAuthBody {
|
||||
public static final String GITHUB = "github";
|
||||
/**
|
||||
* 类别
|
||||
*/
|
||||
private String type;
|
||||
/**
|
||||
* 识别码
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 是否保持登陆
|
||||
*/
|
||||
private boolean rememberMe;
|
||||
}
|
@ -4,23 +4,16 @@
|
||||
|
||||
|
||||
<insert id="addOne" useGeneratedKeys="true" keyColumn="userId" keyProperty="userId">
|
||||
insert into user (username, email, icon, password, createTime, lastLoginTime,version)
|
||||
value
|
||||
(#{username}, #{email}, #{icon}, #{password}, #{createTime}, #{lastLoginTime},#{version})
|
||||
insert into user (username, email, icon, password, createTime, lastLoginTime, version)
|
||||
value
|
||||
(#{username}, #{email}, #{icon}, #{password}, #{createTime}, #{lastLoginTime}, #{version})
|
||||
</insert>
|
||||
|
||||
<select id="selectByUsernameOrEmail" resultType="com.fanxb.bookmark.common.entity.User">
|
||||
select
|
||||
userId,
|
||||
username,
|
||||
email,
|
||||
icon,
|
||||
password,
|
||||
createTime,
|
||||
lastLoginTime,
|
||||
version
|
||||
select *
|
||||
from user
|
||||
where username = #{name} or email = #{email}
|
||||
where username = #{name}
|
||||
or email = #{email}
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
@ -36,10 +29,11 @@
|
||||
where email = #{email}
|
||||
</update>
|
||||
|
||||
<select id="selectByUserId" resultType="com.fanxb.bookmark.common.entity.User">
|
||||
<select id="selectByUserIdOrGithubId" resultType="com.fanxb.bookmark.common.entity.User">
|
||||
select *
|
||||
from user
|
||||
where userId = #{userId}
|
||||
or githubId = #{githubId}
|
||||
</select>
|
||||
|
||||
|
||||
|
@ -19,10 +19,18 @@ import java.util.Map;
|
||||
public class User {
|
||||
|
||||
private int userId;
|
||||
/**
|
||||
* 第三方github登陆id,-1说明非github登陆
|
||||
*/
|
||||
private Long githubId;
|
||||
private String username;
|
||||
private String email;
|
||||
private String newEmail;
|
||||
private String icon;
|
||||
/**
|
||||
* 是否未设置密码
|
||||
*/
|
||||
private Boolean noPassword;
|
||||
@JSONField(serialize = false)
|
||||
private String password;
|
||||
private long createTime;
|
||||
@ -34,4 +42,5 @@ public class User {
|
||||
* 书签同步版本
|
||||
*/
|
||||
private int version;
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,17 @@
|
||||
package com.fanxb.bookmark.common.util;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.fanxb.bookmark.common.exception.CustomException;
|
||||
import okhttp3.*;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -17,13 +23,30 @@ import java.util.concurrent.TimeUnit;
|
||||
* @author fanxb
|
||||
* @date 2019/4/4 15:53
|
||||
*/
|
||||
@Component
|
||||
public class HttpUtil {
|
||||
@Value("${proxy.ip}")
|
||||
private String proxyIp;
|
||||
@Value("${proxy.port}")
|
||||
private int proxyPort;
|
||||
|
||||
private static final int IP_LENGTH = 15;
|
||||
|
||||
public static final OkHttpClient CLIENT = new OkHttpClient.Builder().connectTimeout(3, TimeUnit.SECONDS)
|
||||
.readTimeout(300, TimeUnit.SECONDS).build();
|
||||
private static OkHttpClient CLIENT;
|
||||
public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
|
||||
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
||||
if (StrUtil.isNotBlank(proxyIp)) {
|
||||
builder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyIp, proxyPort)));
|
||||
}
|
||||
CLIENT = builder.connectTimeout(10, TimeUnit.SECONDS)
|
||||
.readTimeout(60, TimeUnit.SECONDS)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能描述: get
|
||||
*
|
||||
@ -142,6 +165,8 @@ public class HttpUtil {
|
||||
String str = res.body().string();
|
||||
if (typeClass.getCanonicalName().equals(JSONObject.class.getCanonicalName())) {
|
||||
return (T) JSONObject.parseObject(str);
|
||||
} else if (typeClass.getCanonicalName().equals(String.class.getCanonicalName())) {
|
||||
return (T) str;
|
||||
} else {
|
||||
return (T) JSONArray.parseArray(str);
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -76,3 +76,18 @@ fileSavePath: ./
|
||||
|
||||
# 服务部署地址
|
||||
serviceAddress: http://localhost
|
||||
|
||||
# 第三方登陆相关
|
||||
OAuth:
|
||||
github:
|
||||
# 客户端id
|
||||
clientId:
|
||||
# 客户端密钥
|
||||
secret:
|
||||
|
||||
# 网络代理(有配置就用代理,未配置不使用代理)
|
||||
proxy:
|
||||
ip:
|
||||
port:
|
||||
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
INSERT INTO `bookmark`.`url`(`method`, `url`, `type`) VALUES ('POST', '/user/oAuthLogin', 0);
|
@ -0,0 +1,14 @@
|
||||
alter table user
|
||||
add githubId bigint default -1 not null comment '-1说明未使用github登陆' after userId;
|
||||
|
||||
create unique index email_index
|
||||
on user (email);
|
||||
|
||||
create index githubIdIndex
|
||||
on user (githubId);
|
||||
|
||||
create index new_email_index
|
||||
on user (newEmail);
|
||||
|
||||
create unique index username_index
|
||||
on user (username);
|
1
bookmark_front/.env.development
Normal file
1
bookmark_front/.env.development
Normal file
@ -0,0 +1 @@
|
||||
VUE_APP_GITHUB_CLIENT_ID=
|
1
bookmark_front/.env.production
Normal file
1
bookmark_front/.env.production
Normal file
@ -0,0 +1 @@
|
||||
VUE_APP_GITHUB_CLIENT_ID=
|
@ -6,7 +6,7 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "App"
|
||||
name: "App",
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -25,5 +25,6 @@ body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: #2c3e50;
|
||||
font-size: 0.16rem;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<div class="main" v-if="userInfo">
|
||||
<a class="ico" href="/"><img src="/static/img/bookmarkLogo.png" /></a>
|
||||
<a-dropdown>
|
||||
<div class="user">
|
||||
@ -31,7 +31,7 @@ export default {
|
||||
await this.$store.dispatch("globalConfig/clear");
|
||||
this.$router.replace("/public/login");
|
||||
} else if (key === "personSpace") {
|
||||
this.$router.push("personSpace/userInfo");
|
||||
this.$router.push("/personSpace/userInfo");
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -8,6 +8,7 @@ import Public from "../views/public/Public.vue";
|
||||
import Login from "../views/public/pages/Login.vue";
|
||||
import Register from "../views/public/pages/Register.vue";
|
||||
import ResetPassword from "../views/public/pages/ResetPassword.vue";
|
||||
import GithubOauth from "../views/public/pages/oauth/Github.vue";
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
@ -47,12 +48,18 @@ const routes = [
|
||||
path: "resetPassword",
|
||||
name: "ResetPassword",
|
||||
component: ResetPassword
|
||||
},
|
||||
{
|
||||
path: "oauth/github",
|
||||
name: "GithubRedirect",
|
||||
component: GithubOauth
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: "history",
|
||||
routes
|
||||
});
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
import localforage from "localforage";
|
||||
import HttpUtil from "../../util/HttpUtil";
|
||||
const USER_INFO = "userInfo";
|
||||
const TOKEN = "token";
|
||||
|
||||
/**
|
||||
* 存储全局配置
|
||||
*/
|
||||
@ -7,11 +10,11 @@ const state = {
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
userInfo: {},
|
||||
[USER_INFO]: {},
|
||||
/**
|
||||
* token
|
||||
*/
|
||||
token: null,
|
||||
[TOKEN]: null,
|
||||
/**
|
||||
* 是否已经初始化完成,避免多次重复初始化
|
||||
*/
|
||||
@ -30,36 +33,43 @@ const actions = {
|
||||
if (context.state.isInit) {
|
||||
return;
|
||||
}
|
||||
context.commit("setUserInfo", await localforage.getItem("userInfo"));
|
||||
const token = await localforage.getItem("token");
|
||||
window.token = token;
|
||||
context.commit("setToken", token);
|
||||
const token = await localforage.getItem(TOKEN);
|
||||
await context.dispatch("setToken", token);
|
||||
|
||||
let userInfo = await localforage.getItem(USER_INFO);
|
||||
if (userInfo === null || userInfo === "") {
|
||||
await context.dispatch("refreshUserInfo");
|
||||
} else {
|
||||
context.commit(USER_INFO, userInfo);
|
||||
}
|
||||
|
||||
context.commit("isInit", true);
|
||||
context.commit("isPhone", /Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent));
|
||||
},
|
||||
async refreshUserInfo({ commit }) {
|
||||
let userInfo = await HttpUtil.get("/user/currentUserInfo");
|
||||
await localforage.setItem("userInfo", userInfo);
|
||||
commit("setUserInfo", userInfo);
|
||||
await localforage.setItem(USER_INFO, userInfo);
|
||||
commit(USER_INFO, userInfo);
|
||||
},
|
||||
async setToken({ commit }, token) {
|
||||
await localforage.setItem(TOKEN, token);
|
||||
commit(TOKEN, token);
|
||||
},
|
||||
//登出清除数据
|
||||
async clear(context) {
|
||||
await localforage.removeItem("userInfo");
|
||||
await localforage.removeItem("token");
|
||||
delete window.token;
|
||||
context.commit("setUserInfo", {});
|
||||
context.commit("setToken", null);
|
||||
context.commit(USER_INFO, null);
|
||||
context.commit(TOKEN, null);
|
||||
context.commit("isInit", false);
|
||||
}
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
setUserInfo(state, userInfo) {
|
||||
localforage.setItem("userInfo", userInfo);
|
||||
userInfo(state, userInfo) {
|
||||
state.userInfo = userInfo;
|
||||
},
|
||||
setToken(state, token) {
|
||||
localforage.setItem("token", token);
|
||||
window.token = token;
|
||||
token(state, token) {
|
||||
state.token = token;
|
||||
},
|
||||
isInit(state, isInit) {
|
||||
|
@ -39,14 +39,14 @@ const getters = {
|
||||
const actions = {
|
||||
//从缓存初始化数据
|
||||
async init(context) {
|
||||
if (context.state.isInit) {
|
||||
if (context.state.isInit || context.state.isIniting) {
|
||||
return;
|
||||
}
|
||||
context.commit("isIniting", true);
|
||||
let userInfo = await httpUtil.get("/user/currentUserInfo");
|
||||
let realVersion = await httpUtil.get("/user/version");
|
||||
let data = await localforage.getItem(TOTAL_TREE_DATA);
|
||||
let version = await localforage.getItem(VERSION);
|
||||
if (!data || userInfo.version > version) {
|
||||
if (!data || realVersion > version) {
|
||||
await context.dispatch("refresh");
|
||||
} else {
|
||||
context.commit(TOTAL_TREE_DATA, data);
|
||||
@ -85,8 +85,8 @@ const actions = {
|
||||
item1.scopedSlots = { title: "nodeTitle" };
|
||||
})
|
||||
);
|
||||
let userInfo = await httpUtil.get("/user/currentUserInfo");
|
||||
await context.dispatch("updateVersion", userInfo.version);
|
||||
let version = await httpUtil.get("/user/version");
|
||||
await context.dispatch("updateVersion", version);
|
||||
context.commit(TOTAL_TREE_DATA, treeData);
|
||||
await localforage.setItem(TOTAL_TREE_DATA, treeData);
|
||||
},
|
||||
@ -243,7 +243,7 @@ const actions = {
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
totalTreeData(state, totalTreeData) {
|
||||
[TOTAL_TREE_DATA]: (state, totalTreeData) => {
|
||||
state.totalTreeData = totalTreeData;
|
||||
},
|
||||
isInit(state, isInit) {
|
||||
@ -253,7 +253,7 @@ const mutations = {
|
||||
state.isIniting = isIniting;
|
||||
},
|
||||
|
||||
version(state, version) {
|
||||
[VERSION]: (state, version) => {
|
||||
state[VERSION] = version;
|
||||
}
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as http from "axios";
|
||||
import { getToken } from "./UserUtil";
|
||||
import vuex from "../store/index.js";
|
||||
import router from "../router/index";
|
||||
|
||||
/**
|
||||
@ -19,7 +19,7 @@ async function request(url, method, params, body, isForm, redirect) {
|
||||
method,
|
||||
params,
|
||||
headers: {
|
||||
"jwt-token": await getToken()
|
||||
"jwt-token": vuex.state.globalConfig.token
|
||||
}
|
||||
};
|
||||
if (isForm) {
|
||||
|
@ -1,26 +1,4 @@
|
||||
import localStore from "localforage";
|
||||
// import HttpUtil from './HttpUtil.js';
|
||||
const TOKEN = "token";
|
||||
// consts USER_INFO = "userInfo";
|
||||
|
||||
/**
|
||||
* 获取用户token
|
||||
*/
|
||||
export async function getToken() {
|
||||
if (!window.token) {
|
||||
window.token = await localStore.getItem(TOKEN);
|
||||
}
|
||||
return window.token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除用户token
|
||||
*/
|
||||
export async function clearToken() {
|
||||
delete window.token;
|
||||
await localStore.removeItem(TOKEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地获取用户信息
|
||||
*/
|
||||
|
@ -52,10 +52,9 @@ export default {
|
||||
return;
|
||||
}
|
||||
this.count = 0;
|
||||
let userInfo = await httpUtil.get("/user/currentUserInfo");
|
||||
this.$store.commit("globalConfig/setUserInfo", userInfo);
|
||||
let version = await httpUtil.get("/user/version");
|
||||
const _this = this;
|
||||
if (this.$store.state.treeData.version < userInfo.version) {
|
||||
if (this.$store.state.treeData.version < version) {
|
||||
this.isOpen = true;
|
||||
this.$confirm({
|
||||
title: "书签数据有更新,是否立即刷新?",
|
||||
|
@ -3,6 +3,7 @@
|
||||
<img class="ico" src="/static/img/bookmarkLogo.png" />
|
||||
<div class="main-body">
|
||||
<router-view />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -34,7 +35,7 @@ export default {
|
||||
width: 2rem;
|
||||
}
|
||||
.main-body {
|
||||
width: 5rem;
|
||||
min-width: 5rem;
|
||||
min-height: 3.5rem;
|
||||
background-color: @publicBgColor;
|
||||
border-radius: 5px;
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<Header current="login" />
|
||||
<div class="form">
|
||||
<a-spin class="form" :delay="100" :spinning="loading">
|
||||
<a-form-model ref="loginForm" :model="form" :rules="rules">
|
||||
<a-form-model-item prop="str" ref="str">
|
||||
<a-input v-model="form.str" placeholder="邮箱/用户名">
|
||||
@ -13,20 +13,24 @@
|
||||
<a-icon slot="prefix" type="password" style="color:rgba(0,0,0,.25)" />
|
||||
</a-input>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item prop="password">
|
||||
<div class="reset">
|
||||
<a-checkbox v-model="form.rememberMe">记住我</a-checkbox>
|
||||
<router-link to="resetPassword" replace>重置密码</router-link>
|
||||
</div>
|
||||
</a-form-model-item>
|
||||
<div class="reset">
|
||||
<a-checkbox v-model="form.rememberMe">记住我</a-checkbox>
|
||||
<router-link to="resetPassword" replace>重置密码</router-link>
|
||||
</div>
|
||||
|
||||
<a-form-model-item>
|
||||
<div class="btns">
|
||||
<a-button type="primary" block @click="submit">登录</a-button>
|
||||
</div>
|
||||
<div class="thirdPart">
|
||||
<span>第三方登陆</span>
|
||||
<a-tooltip title="github登陆" class="oneIcon" placement="bottom">
|
||||
<a-icon type="github" @click="toGithub" style="font-size:1.4em" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</div>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -34,52 +38,109 @@
|
||||
import Header from "@/components/public/Switch.vue";
|
||||
import httpUtil from "../../../util/HttpUtil.js";
|
||||
import { mapMutations } from "vuex";
|
||||
import HttpUtil from "../../../util/HttpUtil.js";
|
||||
export default {
|
||||
name: "Login",
|
||||
components: {
|
||||
Header
|
||||
Header,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
str: "",
|
||||
password: "",
|
||||
rememberMe: false
|
||||
rememberMe: false,
|
||||
},
|
||||
rules: {
|
||||
str: [
|
||||
{ required: true, message: "请输入用户名", trigger: "blur" },
|
||||
{ min: 1, max: 50, message: "最短1,最长50", trigger: "change" }
|
||||
{ min: 1, max: 50, message: "最短1,最长50", trigger: "change" },
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: "请输入密码", trigger: "blur" },
|
||||
{ pattern: "^\\w{6,18}$", message: "密码为6-18位数字,字母,下划线组合", trigger: "change" }
|
||||
]
|
||||
}
|
||||
{ pattern: "^\\w{6,18}$", message: "密码为6-18位数字,字母,下划线组合", trigger: "change" },
|
||||
],
|
||||
},
|
||||
loading: false, //是否加载中
|
||||
oauthLogining: false, //true:正在进行oauth后台操作
|
||||
page: null, //oauth打开的页面实例
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
let _this = this;
|
||||
window.addEventListener("storage", this.storageDeal.bind(this));
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener("storage", this.storageDeal);
|
||||
},
|
||||
methods: {
|
||||
...mapMutations("globalConfig", ["setUserInfo", "setToken"]),
|
||||
submit() {
|
||||
this.$refs.loginForm.validate(async status => {
|
||||
this.$refs.loginForm.validate(async (status) => {
|
||||
if (status) {
|
||||
let res = await httpUtil.post("/user/login", null, this.form);
|
||||
this.setUserInfo(res.user);
|
||||
this.$store.commit("globalConfig/setToken", res.token);
|
||||
this.$router.replace("/");
|
||||
try {
|
||||
this.loading = true;
|
||||
let token = await httpUtil.post("/user/login", null, this.form);
|
||||
this.$store.dispatch("globalConfig/setToken", token);
|
||||
this.$router.replace("/");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
toGithub() {
|
||||
let redirect = location.origin + "/public/oauth/github";
|
||||
let clientId = process.env.VUE_APP_GITHUB_CLIENT_ID;
|
||||
this.page = window.open(`https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirect}`);
|
||||
},
|
||||
async storageDeal(e) {
|
||||
console.log(e);
|
||||
if (e.key === "oauthMessage") {
|
||||
if (this.page != null) {
|
||||
this.page.close();
|
||||
}
|
||||
localStorage.removeItem("oauthMessage");
|
||||
await this.oauthLogin(e.newValue);
|
||||
}
|
||||
},
|
||||
async oauthLogin(form) {
|
||||
if (this.loading) {
|
||||
console.error("正在请求中", form);
|
||||
return;
|
||||
}
|
||||
form = JSON.parse(form);
|
||||
if (!form.code) {
|
||||
this.$message.error("您已拒绝,无法继续登陆");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.loading = true;
|
||||
form.rememberMe = this.form.rememberMe;
|
||||
let token = await HttpUtil.post("/user/oAuthLogin", null, form);
|
||||
this.$store.dispatch("globalConfig/setToken", token);
|
||||
this.$router.replace("/");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.form {
|
||||
margin: 0.3rem;
|
||||
margin-bottom: 0.1rem;
|
||||
.reset {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
.thirdPart {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 1.2em;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
@ -40,7 +40,7 @@ import httpUtil from "../../../util/HttpUtil.js";
|
||||
export default {
|
||||
name: "Login",
|
||||
components: {
|
||||
Header
|
||||
Header,
|
||||
},
|
||||
data() {
|
||||
let repeatPass = (rule, value, cb) => {
|
||||
@ -57,41 +57,40 @@ export default {
|
||||
username: "",
|
||||
email: "",
|
||||
password: "",
|
||||
repeatPass: ""
|
||||
repeatPass: "",
|
||||
},
|
||||
rules: {
|
||||
username: [
|
||||
{ required: true, message: "请输入用户名", trigger: "blur" },
|
||||
{ min: 1, max: 50, message: "最短1,最长50", trigger: "blur" }
|
||||
{ min: 1, max: 50, message: "最短1,最长50", trigger: "blur" },
|
||||
],
|
||||
email: [
|
||||
{
|
||||
pattern: /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
|
||||
message: "请输入正确的邮箱",
|
||||
trigger: "change"
|
||||
}
|
||||
trigger: "change",
|
||||
},
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: "请输入密码", trigger: "blur" },
|
||||
{ pattern: "^\\w{6,18}$", message: "密码为6-18位数字,字母,下划线组合", trigger: "change" }
|
||||
{ pattern: "^\\w{6,18}$", message: "密码为6-18位数字,字母,下划线组合", trigger: "change" },
|
||||
],
|
||||
repeatPass: [{ validator: repeatPass, trigger: "change" }]
|
||||
}
|
||||
repeatPass: [{ validator: repeatPass, trigger: "change" }],
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
let _this = this;
|
||||
this.$refs.registerForm.validate(async status => {
|
||||
this.$refs.registerForm.validate(async (status) => {
|
||||
if (status) {
|
||||
let res = await httpUtil.put("/user", null, _this.form);
|
||||
this.$store.commit("globalConfig/setUserInfo", res.user);
|
||||
this.$store.commit("globalConfig/setToken", res.token);
|
||||
this.$store.dispatch("globalConfig/setToken", res);
|
||||
this.$router.replace("/");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
27
bookmark_front/src/views/public/pages/oauth/Github.vue
Normal file
27
bookmark_front/src/views/public/pages/oauth/Github.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="github">处理中,请稍候。。。</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "GithubRedirect",
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
mounted() {
|
||||
let body = {
|
||||
type: "github",
|
||||
code: this.$route.query.code,
|
||||
};
|
||||
localStorage.setItem("oauthMessage", JSON.stringify(body));
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.github {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user