代码实现微信授权
简单来说,微信授权分为四步:
1.授权登录接口,生成一个二维码在页面中。
2.用户点击授权成功后自动获取code。
3.浏览器自动利用code访问回调接口。
4.在回调接口中利用code登录,appId.appSecret进行登录获取token。
5.利用4获取的token再次访问接口INFO_URL 获取用户微信信息
6.再根据用户微信消息,进行本地信息存储,生成token,并将权限角色等信息返回至前端
7.至此整个微信登录过程结束
/**
* 登录地址
*/
private final static String LOGIN_URL = "https://open.weixin.qq.com/connect/qrconnect?" +
"appid={appId}&" +
"redirect_uri={redirect}&" +
"response_type=code&" +
"scope=snsapi_login&" +
"state={state}";
/**
* 获取微信登录跳转地址
* @return
*/
@RequestMapping(value = "/api/common/wx/get-url", method = { RequestMethod.POST})
@ApiOperation(value = "构建PC端微信登录地址", notes = "获取地址后直接跳转到登录地址,用户进行扫码登录")
public ApiRest<WxUrlRespDTO> getUrl(@RequestBody WxUrlReqDTO reqDTO) {
// state为登录跳转标识,可以跳转到h5页面
String url = LOGIN_URL
.replace("{appId}", wechatConfig.getSiteAppId())
// 统一跳转地址
.replace("{redirect}", baseConfig.getLoginRedirect())
.replace("{state}", reqDTO.getState());
WxUrlRespDTO respDTO = new WxUrlRespDTO();
respDTO.setUrl(url);
return success(respDTO);
}
即 “/api/common/wx/redirect” 该回调接口地址
/**
* 网站的微信登录跳转
* @param code
* @return
*/
@GetMapping("/api/common/wx/redirect")
@ApiOperation(value = "微信PC端登录回调地址")
public ApiRest<SysUserLoginDTO> webRedirect(HttpServletResponse response, @RequestParam("code") String code,
@RequestParam("state") String state) throws IOException {
// 获取会话
SysUserLoginDTO respDTO = wxLoginService.webLogin(
wechatConfig.getSiteAppId(),
wechatConfig.getSiteAppSecret(),
code);
String redirect = "";
// 跳转到H5版本
if(PlatformType.H5.equals(state)){
redirect = baseConfig.getLoginSyncH5();
}
// 跳转到PC版本
if(PlatformType.PC.equals(state)){
redirect = baseConfig.getLoginSyncPc();
}
// 替换地址
redirect = redirect
.replace("{token}", respDTO.getToken())
.replace("{roleType}", String.valueOf(respDTO.getRoleType()));
if(StringUtils.isBlank(redirect)){
return super.failure("同步登录参数错误或跳转地址未配置!");
}
response.sendRedirect(redirect);
return null;
}
会将扫码授权登录成功时返回的code一并携带,请求该回调地址
在该回调接口中,
// 获取会话wxLoginService.webLogin(wechatConfig.getSiteAppId(),wechatConfig.getSiteAppSecret(), code);
通过该方法真正获取微信相关信息
@Override
public SysUserLoginDTO webLogin(String appId, String secret, String code) {
//登录成功获取本人微信的相关信息
WxInfoRespDTO rest = LoginUtils.requestInfo(appId, secret, code);
// 登录并返回
return sysUserService.loginByThird(LoginType.WECHAT, rest.getUqId(), rest.getNickName(), rest.getHeadimgurl());
}
3.1 首先通过该appId,sercet,code,获取微信登录的token信息
/**
* 获取AccessToken
*/
private static final String ACCESS_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={appId}&secret={appSecret}&code={code}&grant_type=authorization_code";
/**
* 获取accessToken
* @param appId
* @param appSecret
* @param code
* @return
*/
public static WxTokenRespDTO requestAccess(String appId, String appSecret, String code){
// 构建完整请求URL
String url = ACCESS_URL.replace("{appId}", appId)
.replace("{appSecret}", appSecret)
.replace("{code}", code);
// 获得返回JSON
String json = HttpClientUtil.getJson(url, null, null);
log.info(json);
// 转换为登录结果
WxTokenRespDTO rest = JSON.parseObject(json, WxTokenRespDTO.class);
return rest;
}
3.2 利用该token再次请求获取微信用户个人相关信息
/**
* 获取用户信息接口
*/
private static final String INFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token={token}&openid={openid}";
/**
* 获取用户头像昵称等内容
* @param appId
* @param appSecret
* @return
*/
public static WxInfoRespDTO requestInfo(String appId, String appSecret, String code){
// 获取accessToken
WxTokenRespDTO token = requestAccess(appId, appSecret, code);
// 获取用户信息
String url = INFO_URL
.replace("{token}", token.getAccessToken())
.replace("{openid}", token.getOpenid());
//发送httpGet请求
String json = HttpClientUtil.getJson(url, null, null);
//解析json结果 转化为Java对象
WxInfoRespDTO rest = JSON.parseObject(json, WxInfoRespDTO.class);
return rest;
}
WxInfoRespDTO 从微信中获取的用户信息:
public class WxInfoRespDTO extends BaseDTO {
@ApiModelProperty(value = "会话标识", required=true)
private String openId;
@ApiModelProperty(value = "昵称", required=true)
private String nickName;
@ApiModelProperty(value = "性别", required=true)
private Integer sex;
@ApiModelProperty(value = "省份", required=true)
private String province;
@ApiModelProperty(value = "市", required=true)
private String city;
@ApiModelProperty(value = "国家", required=true)
private String country;
@ApiModelProperty(value = "头像地址", required=true)
private String headimgurl;
@ApiModelProperty(value = "头像地址", required=true)
private String unionid;
@ApiModelProperty(value = "头像地址", required=true)
private List<String> privilege;
拿到微信登录后的相关信息后,进行本项目的登录
@Override
public SysUserLoginDTO loginByThird(String loginType, String openId, String nickName, String avatar) {
String userId = sysUserBindService.findBind(loginType, openId);
// 不存在,创建新的用户
if (StringUtils.isBlank(userId)) {
// 随机产生数据
SysUserLoginDTO dto = this.saveAndLogin(
RandomStringUtils.randomAlphabetic(16),
"A01",
nickName,
avatar,
RandomStringUtils.randomAlphanumeric(32));
// 建立绑定关系
sysUserBindService.save(false, dto.getId(), loginType, openId);
return dto;
}
// 校验用户状态&密码
SysUser user = this.getById(userId);
return this.checkAndLogin(user, null);
}
/**
* 用户登录校验
*
* @param user
*/
private SysUserLoginDTO checkAndLogin(SysUser user, String password) {
if (user == null) {
throw new ServiceException(ApiError.ERROR_90010001);
}
// 被禁用
if (UserState.DISABLED.equals(user.getState())) {
throw new ServiceException(ApiError.ERROR_90010005);
}
// 待审核
if (UserState.AUDIT.equals(user.getState())) {
throw new ServiceException(ApiError.ERROR_90010006);
}
if (!StringUtils.isBlank(password)) {
boolean pass = PassHandler.checkPass(password, user.getSalt(), user.getPassword());
if (!pass) {
throw new ServiceException(ApiError.ERROR_90010002);
}
}
return this.setToken(user);
}
生成用户的token存储并将用户角色权限等信息,返回至前端
/**
* 保存会话信息
*
* @param user
* @return
*/
@Override
public SysUserLoginDTO setToken(SysUser user) {
// 获取一个用户登录的信息
String key = Constant.USER_NAME_KEY + user.getUserName();
String json = redisService.getString(key);
if (!StringUtils.isBlank(json)) {
// 删除旧的会话
redisService.del(key);
}
SysUserLoginDTO respDTO = new SysUserLoginDTO();
BeanMapper.copy(user, respDTO);
// 正常状态才登录
if(UserState.NORMAL.equals(user.getState())){
// 根据用户生成Token
String token = JwtUtils.sign(user.getUserName());
respDTO.setToken(token);
// 添加角色信息
this.fillRoleData(respDTO);
// 权限表,用于前端控制按钮
List<String> permissions = sysUserRoleService.findUserPermission(user.getId());
respDTO.setPermissions(permissions);
// 保存如Redis
redisService.set(key, JSON.toJSONString(respDTO));
}
return respDTO;
}
到此整个微信登录过程结束