微信小程序登陆
2018年11月7日1.小程序登陆时序图
服务器端(以laravel为主)
1安装EasyWeChat
。
1 2 |
composer require "overtrue/laravel-wechat:~4.0" #安装EasyWeChat php artisan vendor:publish --provider="Overtrue\LaravelWeChat\ServiceProvider" #发布配置文件 |
1.1修改配置文件将config/wechat.php
文件中小程序部分打开。
1 2 3 4 5 6 7 8 9 10 11 |
/* * 小程序 */ 'mini_program' => [ 'default' => [ 'app_id' => env('WECHAT_MINI_PROGRAM_APPID', ''), 'secret' => env('WECHAT_MINI_PROGRAM_SECRET', ''), 'token' => env('WECHAT_MINI_PROGRAM_TOKEN', ''), 'aes_key' => env('WECHAT_MINI_PROGRAM_AES_KEY', ''), ], ], |
1.2编辑.env
配置文件,去微信小程序配置平台哪里获取appid和secret
2.修改表结构
1 |
php artisan make:migration add_weixin_session_key_to_users_table --table=users |
database/migrations/< your_date >add_weixin_session_key_to_users_table.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class AddWeixinSessionKeyToUsersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('users', function (Blueprint $table) { $table->string('weapp_openid')->nullable()->unique()->after('remember_token'); $table->string('weixin_session_key')->nullable()->after('weapp_openid'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn('weapp_openid'); $table->dropColumn('weixin_session_key'); }); } } |
运行迁移命令
1 |
php artisan migrate |
3代码编写
3.1添加路由((使用 dingo api了))
route/api.php
1 2 |
Route ::post('weapp/authorizations', 'Api\AuthorizationsController@weappStore') ->name('api.weapp.authorizations.store'); |
3.2验证类
1 |
php artisan make<span class="token punctuation">:</span>request Api<span class="token operator">/</span>WeappAuthorizationRequest |
开始编辑app/Http/Requests/Api/WeappAuthorizationRequest.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php namespace App\Http\Requests\Api; class WeappAuthorizationRequest extends FormRequest { /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'code' => 'required|string', ]; } } |
3.3控制器(使用 dingo api了)
app/Http/Controllers/Api/AuthorizationsController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
public function weappStore(WeappAuthorizationRequest $request) { $code = $request->code; #获取code $miniProgram = \EasyWeChat::miniProgram(); #根据 code 获取微信 openid 和 session_key $data = $miniProgram->auth->session($code); if (isset($data['errcode'])) { #如果结果错误,说明 code 已过期或不正确,返回 401 错误 return $this->response->errorUnauthorized('code 不正确'); } $user = User::where('weapp_openid', $data['openid'])->first(); #找到 openid 对应的用户 $attributes['weixin_session_key'] = $data['session_key']; if (!$user) { #如果未找到对应用户则需要提交用户名密码进行用户绑定 if (!$request->username) { #如果未提交用户名密码,403 错误提示 return $this->response->errorForbidden('用户不存在'); } $username = $request->username; #用户名可以是邮箱或电话 filter_var($username, FILTER_VALIDATE_EMAIL) ? $credentials['email'] = $username : $credentials['phone'] = $username; $credentials['password'] = $request->password; #验证用户名和密码是否正确 if (!Auth::guard('api')->once($credentials)) { return $this->response->errorUnauthorized('用户名或密码错误'); } #获取对应的用户 $user = Auth::guard('api')->getUser(); $attributes['weapp_openid'] = $data['openid']; } #更新用户数据 $user->update($attributes); #为对应用户创建 JWT $token = Auth::guard('api')->fromUser($user); return $this->respondWithToken($token)->setStatusCode(201); } |
3.4编辑模型user
主要添加weixin_session_key
和weapp_openid
这两个参数
1 2 3 4 5 6 7 |
. . . <span class="token keyword">protected</span> <span class="token variable">$fillable</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token string">'name'</span><span class="token punctuation">,</span> <span class="token string">'phone'</span><span class="token punctuation">,</span> <span class="token string">'email'</span><span class="token punctuation">,</span> <span class="token string">'password'</span><span class="token punctuation">,</span> <span class="token string">'introduction'</span><span class="token punctuation">,</span> <span class="token string">'avatar'</span><span class="token punctuation">,</span><span class="token punctuation">,</span> <span class="token string">'weixin_session_key'</span><span class="token punctuation">,</span> <span class="token string">'weapp_openid'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">; . . .</span> |
客户端(小程序以wepy为例)
1.安装必要组件
获取code,需要安装promise-polyfill
和wepy-async-function
插件
1 2 |
yarn add promise-polyfill //安装生成code的插件 yarn add yarn add wepy-async-function //支持异步的插件 |
2.编写获取code
的测试代码
打开src/app.wpy
增加 this.use('promisify');
使 API promise 化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import <span class="token string">'wepy-async-function'</span> . . . constructor () { super() this.use('requestfix') this.use('promisify') } onLaunch() { wepy.login().then(res => { console.log('login: ', res) }) } . . . |
执行上面的代码会获得一个code 编码
这个code就是小程序客户端需要往服务端发送的值。现在我们可以将onLaunch
方法里面的代码清空
1 2 3 4 5 6 7 8 |
. . . onLaunch() { } . . . |
3 创建登陆页面&配置登陆页面
1 2 |
mkdir src/pages/auth touch src/pages/auth/login.wpy |
打开src/app.wpy
文件并且编辑
1 2 3 4 5 6 7 8 9 10 11 |
. . . pages: [ 'pages/index', 'pages/user', 'pages/auth/login' ], . . . |
4.封装接口请求
创建文件
1 2 |
mkdir src<span class="token operator">/</span>utils touch src<span class="token operator">/</span>utils<span class="token operator">/</span>api<span class="token punctuation">.</span>js |
编辑src/utils/api.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
import wepy from 'wepy' // 服务器接口地址 const host = 'https://wepy.zfajax.com/api' // 普通请求 const request = async (options, showLoading = true) => { // 简化开发,如果传入字符串则转换成 对象 if (typeof options === 'string') { options = { url: options } } // 显示加载中 if (showLoading) { wepy.showLoading({title: '加载中'}) } // 拼接请求地址 options.url = host + '/' + options.url // 调用小程序的 request 方法 let response = await wepy.request(options) if (showLoading) { // 隐藏加载中 wepy.hideLoading() } // 服务器异常后给与提示 if (response.statusCode === 500) { wepy.showModal({ title: '提示', content: '服务器错误,请联系管理员或重试' }) } return response } // 登录 const login = async (params = {}) => { // code 只能使用一次,所以每次单独调用 let loginData = await wepy.login() // 参数中增加code params.code = loginData.code // 接口请求 weapp/authorizations let authResponse = await request({ url: 'weapp/authorizations', data: params, method: 'POST' }) // 登录成功,记录 token 信息 if (authResponse.statusCode === 201) { wepy.setStorageSync('access_token', authResponse.data.access_token) wepy.setStorageSync('access_token_expired_at', new Date().getTime() + authResponse.data.expires_in * 1000) } return authResponse } // 刷新Token const refreshToken = async (accessToken) => { // 请求刷新接口 let refreshResponse = await wepy.request({ url: host + '/' + 'authorizations/current', method: 'PUT', header: { 'Authorization': 'Bearer ' + accessToken } }) // 刷新成功状态码为 200 if (refreshResponse.statusCode === 200) { // 将 Token 及过期时间保存在 storage 中 wepy.setStorageSync('access_token', refreshResponse.data.access_token) wepy.setStorageSync('access_token_expired_at', new Date().getTime() + refreshResponse.data.expires_in * 1000) } return refreshResponse } export default { request, refreshToken, login } |
5编辑登陆页面
编辑src/pages/auth/login.wpy
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
<style lang="less"> .login-wrap { margin-top: 50px; } </style> <template> <view class="page"> <view class="page__bd login-wrap"> <view class="weui-toptips weui-toptips_warn" wx:if="{{ errorMessage }}">{{ errorMessage }}</view> <view class="weui-cells__title">Larabbs 用户登录</view> <view class="weui-cells weui-cells_after-title"> <view class="weui-cell weui-cell_input {{ error ? 'weui-cell_warn' : ''}}"> <view class="weui-cell__hd"> <view class="weui-label">用户名</view> </view> <view class="weui-cell__bd"> <input class="weui-input" placeholder="手机号或邮箱" @input="bindUsernameInput" /> </view> <view wx:if="{{ error }}" class="weui-cell__ft"> <icon type="warn" size="23" color="#E64340"></icon> </view> </view> <view class="weui-cell weui-cell_input {{ error ? 'weui-cell_warn' : ''}}"> <view class="weui-cell__hd"> <view class="weui-label">密码</view> </view> <view class="weui-cell__bd"> <input class="weui-input" placeholder="输入密码" type="password" @input="bindPasswordInput" /> </view> <view wx:if="{{ error }}" class="weui-cell__ft"> <icon type="warn" size="23" color="#E64340"></icon> </view> </view> </view> <view class="weui-btn-area"> <button class="weui-btn" type="primary" @tap="submit">登录</button> </view> </view> </view> </template> <script> import wepy from 'wepy' import api from '@/utils/api' export default class Login extends wepy.page { config = { navigationBarTitleText: '登录' } data = { // 用户名 username: '', // 密码 password: '', // 是否有错 error: false, // 错误信息 errorMessage: '' } methods = { // 绑定用户名 input 变化 bindUsernameInput (e) { this.username = e.detail.value }, // 绑定密码 input 变化 bindPasswordInput (e) { this.password = e.detail.value }, // 表单提交 async submit() { // 提交时重置错误 this.error = false this.errorMessage = '' if (!this.username || !this.password) { this.errorMessage = '请填写账户名和密码' return } // 获取用户名和密码 let params = { username: this.username, password: this.password } try { let authResponse = await api.login(params) // 请求结果为 401 说明用户名和密码错误,显示错误提示 if (authResponse.statusCode === 401) { this.error = true this.errorMessage = authResponse.data.message this.$apply() } // 201 为登录正确,返回上一页 if (authResponse.statusCode === 201) { wepy.navigateBack() } } catch (err) { wepy.showModal({ title: '提示', content: '服务器错误,请联系管理员' }) } } } // 页面打开事件 async onShow() { try { // 打开页面自动调用一次登录 let authResponse = await api.login() // 登录成功返回上一页 if (authResponse.statusCode === 201) { wepy.navigateBack() } } catch (err) { wepy.showModal({ title: '提示', content: '服务器错误,请联系管理员' }) } } } </script> |
6测试
填入服务器上面的存储的用户
和密码
即可。