feat:github登陆接入

This commit is contained in:
fanxb 2021-03-11 16:53:36 +08:00
parent cc71076b45
commit 1196926ac9
29 changed files with 740 additions and 342 deletions

View File

@ -1,9 +1,12 @@
package com.fanxb.bookmark.business.user.controller; package com.fanxb.bookmark.business.user.controller;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.fanxb.bookmark.business.user.vo.LoginBody; import com.fanxb.bookmark.business.user.service.OAuthService;
import com.fanxb.bookmark.business.user.vo.RegisterBody;
import com.fanxb.bookmark.business.user.service.UserService; 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.entity.Result;
import com.fanxb.bookmark.common.util.UserContextHolder; import com.fanxb.bookmark.common.util.UserContextHolder;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@ -22,8 +25,16 @@ import org.springframework.web.multipart.MultipartFile;
@RequestMapping("/user") @RequestMapping("/user")
public class UserController { public class UserController {
private final UserServiceImpl userServiceImpl;
private final OAuthService oAuthService;
private final UserService userService;
@Autowired @Autowired
private UserService userService; public UserController(UserServiceImpl userServiceImpl, OAuthService oAuthService, UserService userService) {
this.userServiceImpl = userServiceImpl;
this.oAuthService = oAuthService;
this.userService = userService;
}
/** /**
* Description: 获取验证码 * Description: 获取验证码
@ -35,7 +46,7 @@ public class UserController {
*/ */
@GetMapping("/authCode") @GetMapping("/authCode")
public Result getAuthCode(@Param("email") String email) { public Result getAuthCode(@Param("email") String email) {
userService.sendAuthCode(email); userServiceImpl.sendAuthCode(email);
return Result.success(null); return Result.success(null);
} }
@ -49,7 +60,7 @@ public class UserController {
*/ */
@PutMapping("") @PutMapping("")
public Result register(@RequestBody RegisterBody body) { 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") @GetMapping("/currentUserInfo")
public Result 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") @PostMapping("/icon")
public Result pushIcon(@RequestParam("file") MultipartFile file) throws Exception { 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") @PostMapping("/login")
public Result login(@RequestBody LoginBody body) { 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") @PostMapping("/resetPassword")
public Result resetPassword(@RequestBody RegisterBody body) { public Result resetPassword(@RequestBody RegisterBody body) {
userService.resetPassword(body); userServiceImpl.resetPassword(body);
return Result.success(null); return Result.success(null);
} }
@ -112,7 +123,7 @@ public class UserController {
*/ */
@PostMapping("/checkPassword") @PostMapping("/checkPassword")
public Result checkPassword(@RequestBody JSONObject obj) { public Result checkPassword(@RequestBody JSONObject obj) {
return Result.success(userService.checkPassword(obj.getString("password"))); return Result.success(userServiceImpl.checkPassword(obj.getString("password")));
} }
@GetMapping("/loginStatus") @GetMapping("/loginStatus")
@ -120,5 +131,28 @@ public class UserController {
return Result.success(null); 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()));
}
} }

View File

@ -3,6 +3,7 @@ package com.fanxb.bookmark.business.user.dao;
import com.fanxb.bookmark.common.entity.User; import com.fanxb.bookmark.common.entity.User;
import com.fanxb.bookmark.common.entity.redis.UserBookmarkUpdate; import com.fanxb.bookmark.common.entity.redis.UserBookmarkUpdate;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update; import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -64,7 +65,7 @@ public interface UserDao {
* @author fanxb * @author fanxb
* @date 2019/7/30 16:08 * @date 2019/7/30 16:08
*/ */
User selectByUserId(int userId); User selectByUserIdOrGithubId(@Param("userId") Integer userId, @Param("githubId") Long githubId);
/** /**
* Description: 更新用户icon * Description: 更新用户icon
@ -103,10 +104,10 @@ public interface UserDao {
* 更新用户新邮箱 * 更新用户新邮箱
* *
* @param userId userId * @param userId userId
* @param newPassword userId * @param newEmail email
*/ */
@Update("update user set newEmail=#{newPassword} where userId= #{userId}") @Update("update user set newEmail=#{newEmail} where userId= #{userId}")
void updateNewEmailByUserId(@Param("userId") int userId, @Param("newPassword") String newPassword); void updateNewEmailByUserId(@Param("userId") int userId, @Param("newEmail") String newEmail);
/** /**
* 新邮箱校验成功更新邮箱 * 新邮箱校验成功更新邮箱
@ -135,4 +136,36 @@ public interface UserDao {
*/ */
@Update("update user set version=version+1") @Update("update user set version=version+1")
void updateAllBookmarkUpdateVersion(); 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);
} }

View File

@ -37,7 +37,7 @@ public class BaseInfoService {
public void changePassword(UpdatePasswordBody body) { public void changePassword(UpdatePasswordBody body) {
int userId = UserContextHolder.get().getUserId(); 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()))) { if (!StrUtil.equals(password, HashUtil.getPassword(body.getOldPassword()))) {
throw new CustomException("旧密码错误"); throw new CustomException("旧密码错误");
} }
@ -66,7 +66,7 @@ public class BaseInfoService {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateEmail(EmailUpdateBody body) { public void updateEmail(EmailUpdateBody body) {
int userId = UserContextHolder.get().getUserId(); 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()))) { if (!StrUtil.equals(oldPassword, HashUtil.getPassword(body.getOldPassword()))) {
throw new CustomException("密码校验失败无法更新email"); throw new CustomException("密码校验失败无法更新email");
} }

View File

@ -1,5 +1,8 @@
package com.fanxb.bookmark.business.user.service; 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);
} }

View File

@ -1,219 +1,43 @@
package com.fanxb.bookmark.business.user.service; package com.fanxb.bookmark.business.user.service;
import com.fanxb.bookmark.business.user.constant.FileConstant; import com.fanxb.bookmark.common.util.TimeUtil;
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 * @author fanxb
* @date 2019/7/5 17:39 * @date 2021/3/11
*/ **/
@Service public interface UserService {
public class UserService { static final String DEFAULT_ICON = "/favicon.ico";
private static final String DEFAULT_ICON = "/favicon.ico";
/** /**
* 短期jwt失效时间 * 短期jwt失效时间
*/ */
private static final long SHORT_EXPIRE_TIME = 2 * 60 * 60 * 1000; static final long SHORT_EXPIRE_TIME = 2 * 60 * 60 * 1000;
/** /**
* 长期jwt失效时间 * 长期jwt失效时间
*/ */
private static final long LONG_EXPIRE_TIME = 30L * TimeUtil.DAY_MS; static final long LONG_EXPIRE_TIME = 30L * TimeUtil.DAY_MS;
/** /**
* 头像文件大小限制 单位KB * 头像文件大小限制 单位KB
*/ */
private static final int ICON_SIZE = 200; static final int ICON_SIZE = 200;
@Autowired /***
private UserDao userDao; * 获取一个可用的用户名
@Autowired
private StringRedisTemplate redisTemplate;
/**
* Description: 向目标发送验证码
*
* @param email 目标
* @author fanxb * @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 * @return java.lang.String
* @date 2021/3/11
**/
String createNewUsername();
/**
* 获取当前用户的version
*
* @return int
* @author fanxb * @author fanxb
* @date 2019/11/11 23:41 * @date 2021/3/11
*/ **/
public String checkPassword(String password) { int getCurrentUserVersion(int userId);
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;
}
} }

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -10,17 +10,10 @@
</insert> </insert>
<select id="selectByUsernameOrEmail" resultType="com.fanxb.bookmark.common.entity.User"> <select id="selectByUsernameOrEmail" resultType="com.fanxb.bookmark.common.entity.User">
select select *
userId,
username,
email,
icon,
password,
createTime,
lastLoginTime,
version
from user from user
where username = #{name} or email = #{email} where username = #{name}
or email = #{email}
limit 1 limit 1
</select> </select>
@ -36,10 +29,11 @@
where email = #{email} where email = #{email}
</update> </update>
<select id="selectByUserId" resultType="com.fanxb.bookmark.common.entity.User"> <select id="selectByUserIdOrGithubId" resultType="com.fanxb.bookmark.common.entity.User">
select * select *
from user from user
where userId = #{userId} where userId = #{userId}
or githubId = #{githubId}
</select> </select>

View File

@ -19,10 +19,18 @@ import java.util.Map;
public class User { public class User {
private int userId; private int userId;
/**
* 第三方github登陆id,-1说明非github登陆
*/
private Long githubId;
private String username; private String username;
private String email; private String email;
private String newEmail; private String newEmail;
private String icon; private String icon;
/**
* 是否未设置密码
*/
private Boolean noPassword;
@JSONField(serialize = false) @JSONField(serialize = false)
private String password; private String password;
private long createTime; private long createTime;
@ -34,4 +42,5 @@ public class User {
* 书签同步版本 * 书签同步版本
*/ */
private int version; private int version;
} }

View File

@ -1,11 +1,17 @@
package com.fanxb.bookmark.common.util; package com.fanxb.bookmark.common.util;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.fanxb.bookmark.common.exception.CustomException; import com.fanxb.bookmark.common.exception.CustomException;
import okhttp3.*; import okhttp3.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -17,13 +23,30 @@ import java.util.concurrent.TimeUnit;
* @author fanxb * @author fanxb
* @date 2019/4/4 15:53 * @date 2019/4/4 15:53
*/ */
@Component
public class HttpUtil { public class HttpUtil {
@Value("${proxy.ip}")
private String proxyIp;
@Value("${proxy.port}")
private int proxyPort;
private static final int IP_LENGTH = 15; private static final int IP_LENGTH = 15;
public static final OkHttpClient CLIENT = new OkHttpClient.Builder().connectTimeout(3, TimeUnit.SECONDS) private static OkHttpClient CLIENT;
.readTimeout(300, TimeUnit.SECONDS).build();
public static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); 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 * 功能描述: get
* *
@ -142,6 +165,8 @@ public class HttpUtil {
String str = res.body().string(); String str = res.body().string();
if (typeClass.getCanonicalName().equals(JSONObject.class.getCanonicalName())) { if (typeClass.getCanonicalName().equals(JSONObject.class.getCanonicalName())) {
return (T) JSONObject.parseObject(str); return (T) JSONObject.parseObject(str);
} else if (typeClass.getCanonicalName().equals(String.class.getCanonicalName())) {
return (T) str;
} else { } else {
return (T) JSONArray.parseArray(str); return (T) JSONArray.parseArray(str);
} }

View File

@ -76,3 +76,18 @@ fileSavePath: ./
# 服务部署地址 # 服务部署地址
serviceAddress: http://localhost serviceAddress: http://localhost
# 第三方登陆相关
OAuth:
github:
# 客户端id
clientId:
# 客户端密钥
secret:
# 网络代理(有配置就用代理,未配置不使用代理)
proxy:
ip:
port:

View File

@ -0,0 +1 @@
INSERT INTO `bookmark`.`url`(`method`, `url`, `type`) VALUES ('POST', '/user/oAuthLogin', 0);

View File

@ -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);

View File

@ -0,0 +1 @@
VUE_APP_GITHUB_CLIENT_ID=

View File

@ -0,0 +1 @@
VUE_APP_GITHUB_CLIENT_ID=

View File

@ -6,7 +6,7 @@
<script> <script>
export default { export default {
name: "App" name: "App",
}; };
</script> </script>
@ -25,5 +25,6 @@ body {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
color: #2c3e50; color: #2c3e50;
font-size: 0.16rem;
} }
</style> </style>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="main"> <div class="main" v-if="userInfo">
<a class="ico" href="/"><img src="/static/img/bookmarkLogo.png" /></a> <a class="ico" href="/"><img src="/static/img/bookmarkLogo.png" /></a>
<a-dropdown> <a-dropdown>
<div class="user"> <div class="user">
@ -31,7 +31,7 @@ export default {
await this.$store.dispatch("globalConfig/clear"); await this.$store.dispatch("globalConfig/clear");
this.$router.replace("/public/login"); this.$router.replace("/public/login");
} else if (key === "personSpace") { } else if (key === "personSpace") {
this.$router.push("personSpace/userInfo"); this.$router.push("/personSpace/userInfo");
} }
}, },
}, },

View File

@ -8,6 +8,7 @@ import Public from "../views/public/Public.vue";
import Login from "../views/public/pages/Login.vue"; import Login from "../views/public/pages/Login.vue";
import Register from "../views/public/pages/Register.vue"; import Register from "../views/public/pages/Register.vue";
import ResetPassword from "../views/public/pages/ResetPassword.vue"; import ResetPassword from "../views/public/pages/ResetPassword.vue";
import GithubOauth from "../views/public/pages/oauth/Github.vue";
Vue.use(VueRouter); Vue.use(VueRouter);
@ -47,12 +48,18 @@ const routes = [
path: "resetPassword", path: "resetPassword",
name: "ResetPassword", name: "ResetPassword",
component: ResetPassword component: ResetPassword
},
{
path: "oauth/github",
name: "GithubRedirect",
component: GithubOauth
} }
] ]
} }
]; ];
const router = new VueRouter({ const router = new VueRouter({
mode: "history",
routes routes
}); });

View File

@ -1,5 +1,8 @@
import localforage from "localforage"; import localforage from "localforage";
import HttpUtil from "../../util/HttpUtil"; import HttpUtil from "../../util/HttpUtil";
const USER_INFO = "userInfo";
const TOKEN = "token";
/** /**
* 存储全局配置 * 存储全局配置
*/ */
@ -7,11 +10,11 @@ const state = {
/** /**
* 用户信息 * 用户信息
*/ */
userInfo: {}, [USER_INFO]: {},
/** /**
* token * token
*/ */
token: null, [TOKEN]: null,
/** /**
* 是否已经初始化完成,避免多次重复初始化 * 是否已经初始化完成,避免多次重复初始化
*/ */
@ -30,36 +33,43 @@ const actions = {
if (context.state.isInit) { if (context.state.isInit) {
return; return;
} }
context.commit("setUserInfo", await localforage.getItem("userInfo")); const token = await localforage.getItem(TOKEN);
const token = await localforage.getItem("token"); await context.dispatch("setToken", token);
window.token = token;
context.commit("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("isInit", true);
context.commit("isPhone", /Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)); context.commit("isPhone", /Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent));
}, },
async refreshUserInfo({ commit }) { async refreshUserInfo({ commit }) {
let userInfo = await HttpUtil.get("/user/currentUserInfo"); let userInfo = await HttpUtil.get("/user/currentUserInfo");
await localforage.setItem("userInfo", userInfo); await localforage.setItem(USER_INFO, userInfo);
commit("setUserInfo", userInfo); commit(USER_INFO, userInfo);
},
async setToken({ commit }, token) {
await localforage.setItem(TOKEN, token);
commit(TOKEN, token);
}, },
//登出清除数据 //登出清除数据
async clear(context) { async clear(context) {
await localforage.removeItem("userInfo"); await localforage.removeItem("userInfo");
await localforage.removeItem("token"); await localforage.removeItem("token");
delete window.token; context.commit(USER_INFO, null);
context.commit("setUserInfo", {}); context.commit(TOKEN, null);
context.commit("setToken", null); context.commit("isInit", false);
} }
}; };
const mutations = { const mutations = {
setUserInfo(state, userInfo) { userInfo(state, userInfo) {
localforage.setItem("userInfo", userInfo);
state.userInfo = userInfo; state.userInfo = userInfo;
}, },
setToken(state, token) { token(state, token) {
localforage.setItem("token", token);
window.token = token;
state.token = token; state.token = token;
}, },
isInit(state, isInit) { isInit(state, isInit) {

View File

@ -39,14 +39,14 @@ const getters = {
const actions = { const actions = {
//从缓存初始化数据 //从缓存初始化数据
async init(context) { async init(context) {
if (context.state.isInit) { if (context.state.isInit || context.state.isIniting) {
return; return;
} }
context.commit("isIniting", true); 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 data = await localforage.getItem(TOTAL_TREE_DATA);
let version = await localforage.getItem(VERSION); let version = await localforage.getItem(VERSION);
if (!data || userInfo.version > version) { if (!data || realVersion > version) {
await context.dispatch("refresh"); await context.dispatch("refresh");
} else { } else {
context.commit(TOTAL_TREE_DATA, data); context.commit(TOTAL_TREE_DATA, data);
@ -85,8 +85,8 @@ const actions = {
item1.scopedSlots = { title: "nodeTitle" }; item1.scopedSlots = { title: "nodeTitle" };
}) })
); );
let userInfo = await httpUtil.get("/user/currentUserInfo"); let version = await httpUtil.get("/user/version");
await context.dispatch("updateVersion", userInfo.version); await context.dispatch("updateVersion", version);
context.commit(TOTAL_TREE_DATA, treeData); context.commit(TOTAL_TREE_DATA, treeData);
await localforage.setItem(TOTAL_TREE_DATA, treeData); await localforage.setItem(TOTAL_TREE_DATA, treeData);
}, },
@ -243,7 +243,7 @@ const actions = {
}; };
const mutations = { const mutations = {
totalTreeData(state, totalTreeData) { [TOTAL_TREE_DATA]: (state, totalTreeData) => {
state.totalTreeData = totalTreeData; state.totalTreeData = totalTreeData;
}, },
isInit(state, isInit) { isInit(state, isInit) {
@ -253,7 +253,7 @@ const mutations = {
state.isIniting = isIniting; state.isIniting = isIniting;
}, },
version(state, version) { [VERSION]: (state, version) => {
state[VERSION] = version; state[VERSION] = version;
} }
}; };

View File

@ -1,5 +1,5 @@
import * as http from "axios"; import * as http from "axios";
import { getToken } from "./UserUtil"; import vuex from "../store/index.js";
import router from "../router/index"; import router from "../router/index";
/** /**
@ -19,7 +19,7 @@ async function request(url, method, params, body, isForm, redirect) {
method, method,
params, params,
headers: { headers: {
"jwt-token": await getToken() "jwt-token": vuex.state.globalConfig.token
} }
}; };
if (isForm) { if (isForm) {

View File

@ -1,26 +1,4 @@
import localStore from "localforage";
// import HttpUtil from './HttpUtil.js';
const TOKEN = "token"; 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);
}
/** /**
* 本地获取用户信息 * 本地获取用户信息
*/ */

View File

@ -52,10 +52,9 @@ export default {
return; return;
} }
this.count = 0; this.count = 0;
let userInfo = await httpUtil.get("/user/currentUserInfo"); let version = await httpUtil.get("/user/version");
this.$store.commit("globalConfig/setUserInfo", userInfo);
const _this = this; const _this = this;
if (this.$store.state.treeData.version < userInfo.version) { if (this.$store.state.treeData.version < version) {
this.isOpen = true; this.isOpen = true;
this.$confirm({ this.$confirm({
title: "书签数据有更新,是否立即刷新?", title: "书签数据有更新,是否立即刷新?",

View File

@ -3,6 +3,7 @@
<img class="ico" src="/static/img/bookmarkLogo.png" /> <img class="ico" src="/static/img/bookmarkLogo.png" />
<div class="main-body"> <div class="main-body">
<router-view /> <router-view />
</div> </div>
</div> </div>
</template> </template>
@ -34,7 +35,7 @@ export default {
width: 2rem; width: 2rem;
} }
.main-body { .main-body {
width: 5rem; min-width: 5rem;
min-height: 3.5rem; min-height: 3.5rem;
background-color: @publicBgColor; background-color: @publicBgColor;
border-radius: 5px; border-radius: 5px;

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<Header current="login" /> <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 ref="loginForm" :model="form" :rules="rules">
<a-form-model-item prop="str" ref="str"> <a-form-model-item prop="str" ref="str">
<a-input v-model="form.str" placeholder="邮箱/用户名"> <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-icon slot="prefix" type="password" style="color:rgba(0,0,0,.25)" />
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item prop="password">
<div class="reset"> <div class="reset">
<a-checkbox v-model="form.rememberMe">记住我</a-checkbox> <a-checkbox v-model="form.rememberMe">记住我</a-checkbox>
<router-link to="resetPassword" replace>重置密码</router-link> <router-link to="resetPassword" replace>重置密码</router-link>
</div> </div>
</a-form-model-item>
<a-form-model-item> <a-form-model-item>
<div class="btns"> <div class="btns">
<a-button type="primary" block @click="submit">登录</a-button> <a-button type="primary" block @click="submit">登录</a-button>
</div> </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-item>
</a-form-model> </a-form-model>
</div> </a-spin>
</div> </div>
</template> </template>
@ -34,52 +38,109 @@
import Header from "@/components/public/Switch.vue"; import Header from "@/components/public/Switch.vue";
import httpUtil from "../../../util/HttpUtil.js"; import httpUtil from "../../../util/HttpUtil.js";
import { mapMutations } from "vuex"; import { mapMutations } from "vuex";
import HttpUtil from "../../../util/HttpUtil.js";
export default { export default {
name: "Login", name: "Login",
components: { components: {
Header Header,
}, },
data() { data() {
return { return {
form: { form: {
str: "", str: "",
password: "", password: "",
rememberMe: false rememberMe: false,
}, },
rules: { rules: {
str: [ str: [
{ required: true, message: "请输入用户名", trigger: "blur" }, { required: true, message: "请输入用户名", trigger: "blur" },
{ min: 1, max: 50, message: "最短1最长50", trigger: "change" } { min: 1, max: 50, message: "最短1最长50", trigger: "change" },
], ],
password: [ password: [
{ required: true, message: "请输入密码", trigger: "blur" }, { 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: { methods: {
...mapMutations("globalConfig", ["setUserInfo", "setToken"]), ...mapMutations("globalConfig", ["setUserInfo", "setToken"]),
submit() { submit() {
this.$refs.loginForm.validate(async status => { this.$refs.loginForm.validate(async (status) => {
if (status) { if (status) {
let res = await httpUtil.post("/user/login", null, this.form); try {
this.setUserInfo(res.user); this.loading = true;
this.$store.commit("globalConfig/setToken", res.token); let token = await httpUtil.post("/user/login", null, this.form);
this.$store.dispatch("globalConfig/setToken", token);
this.$router.replace("/"); 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> </script>
<style lang="less" scoped> <style lang="less" scoped>
.form { .form {
margin: 0.3rem; margin: 0.3rem;
margin-bottom: 0.1rem;
.reset { .reset {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
} }
.thirdPart {
display: flex;
justify-content: space-between;
font-size: 1.2em;
align-items: center;
}
</style> </style>

View File

@ -40,7 +40,7 @@ import httpUtil from "../../../util/HttpUtil.js";
export default { export default {
name: "Login", name: "Login",
components: { components: {
Header Header,
}, },
data() { data() {
let repeatPass = (rule, value, cb) => { let repeatPass = (rule, value, cb) => {
@ -57,41 +57,40 @@ export default {
username: "", username: "",
email: "", email: "",
password: "", password: "",
repeatPass: "" repeatPass: "",
}, },
rules: { rules: {
username: [ username: [
{ required: true, message: "请输入用户名", trigger: "blur" }, { required: true, message: "请输入用户名", trigger: "blur" },
{ min: 1, max: 50, message: "最短1最长50", trigger: "blur" } { min: 1, max: 50, message: "最短1最长50", trigger: "blur" },
], ],
email: [ 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,}))$/, 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: "请输入正确的邮箱", message: "请输入正确的邮箱",
trigger: "change" trigger: "change",
} },
], ],
password: [ password: [
{ required: true, message: "请输入密码", trigger: "blur" }, { 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: { methods: {
submit() { submit() {
let _this = this; let _this = this;
this.$refs.registerForm.validate(async status => { this.$refs.registerForm.validate(async (status) => {
if (status) { if (status) {
let res = await httpUtil.put("/user", null, _this.form); let res = await httpUtil.put("/user", null, _this.form);
this.$store.commit("globalConfig/setUserInfo", res.user); this.$store.dispatch("globalConfig/setToken", res);
this.$store.commit("globalConfig/setToken", res.token);
this.$router.replace("/"); this.$router.replace("/");
} }
}); });
} },
} },
}; };
</script> </script>

View 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>