- 前端门户点击微信登录扫码发起微信授权请求
- 跳转回调页面解析url中的code和state
- 将code与绑定页面的url封装成参数传递到后台
- 向后台发送登录请求
- 后台初始controller用post请求方法接收参数,用map来接收传过来的参数,避免创建对象。
- 调用service层微信登录方法
- service层做处理获取参数code授权码和绑定页面url地址binderUrl
- 然后准备请求,获取access_token,和openid
- 通过准备的httpClient工具类来发送请求,获取
- 接收发送请求获取的json字符串
- 将json字符串转化成json对象
- 通过json对象获取access_token和openid
- 然后通过openid查询微信用户
- 如果微信用户不为空并且微信用户的字段User_id不为空就是已经绑定了用户的
- 直接免密登录
- 成功一个随机数uToken将uToken和user存到redis里面,并设置过期时间
- 注意要将user转换成功json字符串对象才可以存到redis里面
- 然后使用map对象添加到map中传到前台
- 返回成功并且传uToken和user到前台
- 前台就弹出提示登录成功,并且将uToken和user都存在localstoage里面。然后跳转主页
- 如果通过openid查询的微信用户为空就返回失败并且传值bindderUrl到前台,
- 注意binderUrl地址将access_token和openid传到前台去了
- 前台接收到返回类型是失败就跳转绑定页面进行绑定。
- 前台绑定页面输入用户名和密码进行绑定
- 将参数username,password,还有通过url传到前台的access_token,openid封装到一个变量里面传到后台。
- 发送绑定请求,并传参
- 后台controller响应请求使用map对象接收参数
- 调用service绑定方法
- service层接收参数username,password,accessToken,openid
- 准备url请求,获取微信用户信息
- 通过准备的工具类发送请求,接收参数
- 将接收的json字符串转换成json对象
- 将json对象转换成功WechatUser对象
- 通过参数username去查询数据库是否有该用户
- 如果有就去判断密码输入是否正确
- 输入的密码加上数据库的盐值通过MD5加密和数据库的密码比较
- 如果密码相同就免密登录
- 不同就返回false
- 如果用户不存在那就新建一个用户
- 绑定微信用户字段的user_id
- 最后免密登录
- 随机生成一个token
- 将token和user存在redis中并且设置过期时间
- 使用map对象将token和user传到前台
- 返回成功并传递token和user
- 前台将token和user存在localstoage里面
- 跳转主页
主要代码如下
Controller层
/**
* 微信登录
*/
@PostMapping("/weChat")
public AjaxResult weChatLogin(@RequestBody Map<String,String> map){
return userService.weChatLogin(map);
}
/**
* 绑定用户
*/
@PostMapping("/binder")
public AjaxResult Binder(@RequestBody Map<String,String> params){
return userService.binder(params);
}
Service层
/**
* 微信登录
* @param map
* @return
*/
@Override
public AjaxResult weChatLogin(Map<String, String> map) {
//获取参数 code binder
String code = map.get("code");
String binderUrl = map.get("binderUrl");
//发送请求获取openid和token
//准备url
String url = WechatConstant.GET_OPENID_URL.replace("APPID", WechatConstant.APPID)
.replace("SECRET", WechatConstant.SECURITY).replace("CODE", code);
//System.out.println(url);
//发送请求 返回一个json字符串
String jsonStr = HttpClientUtil.htppGet(url);
//把字符串转换成json
JSONObject jsonObject = JSONObject.parseObject(jsonStr);
System.out.println(jsonObject);
//获取 access_token和openid
String access_token = jsonObject.getString("access_token");
String openid = jsonObject.getString("openid");
//判断用户是否已经绑定
//通过openid查询微信用户 然后在去查询微信用户的user_id是否有值,如果有值就说明绑定了的 没有值就没有绑定
WeChatUser weChatUser=weChatUserService.getWeChatUserByOpenId(openid);
if(weChatUser!=null&&weChatUser.getUser_id()!=null){
//通过user_id查询用户
User user = userMapper.loadById(weChatUser.getUser_id());
//把uToken存放在redislimian
String uToken = UUID.randomUUID().toString();
//存在redis JSONObject.toJSONString(user)把java对象转化成json格式的字符串
RedisUtils.INSTANCE.set(uToken,JSONObject.toJSONString(user),30*60);
//把uToken和user都返回给前台
Map<String,Object> resultObj=new HashMap<>();
resultObj.put("uToken",uToken);
resultObj.put("user",user);
return AjaxResult.me().setResultObj(resultObj);
}else {
//跳转绑定界面
binderUrl=binderUrl+"?access_token="+access_token+"&openId="+openid;
return AjaxResult.me().setSuccess(false).setMessage("binder").setResultObj(binderUrl);
}
}
/**
* 微信登录绑定用户
* @param params
* @return
*/
@Override
public AjaxResult binder(Map<String, String> params) {
//获取参数
String username = params.get("username");
String password = params.get("password");
String accessToken = params.get("accessToken");
String openId = params.get("openId");
//获取微信用户
//准备url发送获取
String url = WechatConstant.GET_WXUSER_URL.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);
//发送请求获取微信用户
String JsonStr = HttpClientUtil.htppGet(url);
//将JsonStr转换成微信用户
WeChatUser weChatUser=JsonStr2WeChatUser(JsonStr);
//通过用户名去查询用户 如果有就验证密码直接绑定
User user = userMapper.loadByUsername(username);
if(user!=null){
String salt = user.getSalt();
String wxpassword = MD5Utils.encrypByMd5(password + salt);
if(!user.getPassword().equals(wxpassword)) {
return AjaxResult.me().setSuccess(false).setMessage("用户或者密码不正确!");
}
}else {
//没有用户就创建新的用户 获取id绑定微信的user_id
User newUser = new User();
newUser.setUsername(username);
//随机生成一个盐值
String salt = StrUtils.getComplexRandomString(32);
newUser.setPassword(MD5Utils.encrypByMd5(password+salt));
newUser.setSalt(salt);
//激活
newUser.setState(1);
newUser.setCreatetime(new Date());
userMapper.save(newUser);
user=newUser;
}
//绑定用户
weChatUser.setUser_id(user.getId());
weChatUserService.save(weChatUser);
//免密登录
//生成一个uToken
String uToken = UUID.randomUUID().toString();
//将令牌和用户信息带到前台去和存到redis
RedisUtils.INSTANCE.set(uToken,JSONObject.toJSONString(user),30*60);
Map<String,Object> map=new HashMap<>();
map.put("uToken",uToken);
map.put("user",user);
return AjaxResult.me().setResultObj(map);
}
/**
* 把json字符串转成微信用户
* @param jsonStr
* @return
*/
private WeChatUser JsonStr2WeChatUser(String jsonStr) {
/**
* 微信返回的微信用户信息
*/
WeChatUser weChatUser = new WeChatUser();
//把jsonStr转换成对象
JSONObject jsonObject = JSONObject.parseObject(jsonStr);
weChatUser.setAddress(jsonObject.getString("country")+jsonObject.getString("province")
+jsonObject.getString("city"));
weChatUser.setHeadimgurl(jsonObject.getString("headimgurl"));
weChatUser.setNickname(jsonObject.getString("nickname"));
weChatUser.setOpenid(jsonObject.getString("openid"));
return weChatUser;
}
- 登录页面前台
<li><a :href="WechatUrl"><i class="am-icon-weixin am-icon-sm"></i><span>微信登录</span> </a></li>
//绑定的值
WechatUrl: 'https://open.weixin.qq.com/connect/qrconnect?appid=wxd853562a0548a7d0' +
'&redirect_uri=http://bugtracker.itsource.cn/callback.html&response_type=code&scope=snsapi_login&state=1#wechat_redirect',
回调页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>回调地址</title>
<!--引入Vue-->
<script src="plugins/vue/dist/vue.min.js"></script>
<!--引入axios-->
<script src="plugins/axios/dist/axios.min.js"></script>
<!--给Vue添加属性$http-->
<!--配置axios全局路径-->
<script src="js/common.js"></script>
</head>
<body>
<div id="myDiv">
</div>
</body>
<script>
new Vue({
el:"#myDiv",
data:{},
methods:{
},
mounted(){
let obj = parseUrl2Obj(location.href);
//封装参数携带给后台处理
let parms={
code:obj.code,
binderUrl:"http://bugtracker.itsource.cn/binder.html"
}
//发送请求,做微信登录
this.$http.post("/user/weChat",parms).then(result=>{
//使用解析表达式接收值
let{message,success,resultObj}=result.data;
if(success){
//返回成功就把utoken和user存放在localstorage中跳转登录页面
//存放utoken
localStorage.setItem("uToken",resultObj.uToken);
localStorage.setItem("user",resultObj.user);
//跳转主页去
location.href="/index.html";
return;
}
//不成功就跳转绑定页面
if(message=="binder"){
//跳转绑定页面 resultObj:后台返回的一个url
location.href=resultObj;
}
})
}
})
</script>
</html>
绑定页面
<!DOCTYPE html>
<html xmlns:v-on="http://www.w3.org/1999/xhtml">
<head lang="en">
<meta charset="UTF-8">
<title>绑定</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="renderer" content="webkit">
<meta http-equiv="Cache-Control" content="no-siteapp" />
<link rel="stylesheet" href="../AmazeUI-2.4.2/assets/css/amazeui.css" />
<link href="../css/dlstyle.css" rel="stylesheet" type="text/css">
<!--导入vue和axios-->
<script src="./plugins/vue/dist/vue.min.js"></script>
<script src="./plugins/axios/dist/axios.js"></script>
<script src="./js/common.js"></script>
</head>
<body>
<div id="loginPage">
<div class="login-boxtitle">
<a href="home.html"><img src="res/static/img/logo.png"></a>
</div>
<div class="login-banner">
<div class="login-main">
<div class="login-banner-bg"><span></span><img src="../images/big.jpg" /></div>
<div class="login-box">
<h3 class="title">绑定用户</h3>
<div class="clear"></div>
<div class="login-form">
<form method="post">
<div class="user-name">
<label for="user"><i class="am-icon-user"></i></label>
<input type="text" v-model="username" name="username" id="user" placeholder="请输入用户名">
</div>
<div class="user-pass">
<label for="password"><i class="am-icon-lock"></i></label>
<input type="password" v-model="password" name="" id="password" placeholder="请输入密码">
</div>
</form>
</div>
<div class="am-cf">
<input type="button" name="" v-on:click="handleBinder()" value="绑 定" class="am-btn am-btn-primary am-btn-sm">
</div>
</div>
</div>
</div>
<div class="footer ">
<div class="footer-hd ">
<p>
<a href="# ">恒望科技</a>
<b>|</b>
<a href="# ">商城首页</a>
<b>|</b>
<a href="# ">支付宝</a>
<b>|</b>
<a href="# ">物流</a>
</p>
</div>
<div class="footer-bd ">
<p>
<a href="# ">关于恒望</a>
<a href="# ">合作伙伴</a>
<a href="# ">联系我们</a>
<a href="# ">网站地图</a>
<em>© 2015-2025 Hengwang.com 版权所有. 更多模板 <a href="http://www.cssmoban.com/" target="_blank" title="模板之家">模板之家</a> - Collect from <a href="http://www.cssmoban.com/" title="网页模板" target="_blank">网页模板</a></em>
</p>
</div>
</div>
</div>
<script type="text/javascript">
new Vue({
el:"#loginPage",
data(){
return {
username:'',
password:'',
}
},
methods:{
handleBinder(){
let paramsObj = parseUrl2Obj(location.href);
var bindParams = {
username: this.username,
password: this.password,
//以下参数从地址栏解析
accessToken: paramsObj.access_token,
openId: paramsObj.openId
};
this.$http.post("/user/binder",bindParams).then(result => {
//success messaage resultObj
//解构表达式
let {success,message,resultObj}= result.data;
if(!success){
alert(message);
return;
}
//提供成功
alert("登录成功!");
//登录成功 存放token和用户到localstorage
localStorage.setItem("uToken",resultObj.uToken)
localStorage.setItem("user",resultObj.user)
//跳转主页
location.href = "/index.html"
});
}
}
});
</script>
</body>
</html>