1.判断是否登录,如果登录了,就让其直接访问.没有登录跳转到登录授权的方法
在laravel中代码实现
a.让需要登录的所有路由使用中间件包裹
//授权登陆
Route::get('auth/login', 'Front\AuthController@getLogin')->name('login');
//需要登录才可以访问的网页
Route::group([ 'middleware' => 'auth', 'namespace' => 'Front'], function(){
.......
});
//其他不需要登录可以访问的网页
......
b.中间件判断登录代码
public function handle($request, Closure $next)
{
if (!\Auth::check()){//判断登录
//没有登录
if ($request->ajax()) {//如果是异步
return response('Unauthorized.', 401);
} else {
return redirect('auth/login');//跳转到授权登录的方法
}
}
//登录继续执行
return $next($request);
}
}
2.授权登录的逻辑
a.判断网页来源,是自己跳转还是微信返回的,发送请求 构造链接 目的是为了获取code
$state=$request->state;//初始的时候没有值,点确定按钮后是,submit,获取到code后,get_code
//只有从微信端跳转过来的链接 才可以进入if
if($state!='get_code'){
//发送获取code请求
$config = Config::find(1);//从数据库取出来corpid 和 agent_id
//判断网站是http 还是https
if(isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on')
{
$redirect_uri = urlencode('https://'.$_SERVER['HTTP_HOST'].'/auth/login');
//echo "HTTPS";
}else{
//echo "HTTP";
$redirect_uri = urlencode('http://'.$_SERVER['HTTP_HOST'].'/auth/login');
}
//snsapi_userinfo 此处使用静默授权
$url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=$config->corpid&redirect_uri=$redirect_uri&response_type=code&scope=snsapi_userinfo&agentid=$config->agent_id&state=get_code#wechat_redirect";//构造链接
return redirect($url);//跳转链接
}
//页面将跳转至 redirect_uri?code=CODE&state=STATE
//处理请求结果
$code = $request->code;
$qywx =new \App\Libs\QyWeixinClass();//此处代码实现会在文章最后给出
$accessToken = $qywx->getAgentAccessToken();
$url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=$accessToken&code=$code";
$res = $qywx->httpGet($url);
$res = json_decode($res);
b.返回结果有两种
- 当该用户为企业成员时返回
{
"errcode": 0,
"errmsg": "ok",
"UserId":"USERID",
"DeviceId":"DEVICEID",
"user_ticket": "USER_TICKET",
"expires_in":7200
}
- 非企业成员授权时返回
{
"errcode": 0,
"errmsg": "ok",
"OpenId":"OPENID",
"DeviceId":"DEVICEID"
}
c.根据自己的业务逻辑需求去调整应该怎么处理企业用户和非企业用户
判断请求是否正确返回数据
if (''==$res||$res->errcode != 0){
//请求没返回正确数据的逻辑
.....
}
//判断是否是企业用户
if (!isset($res->UserId)){
//不是企业用户的逻辑
......
}
d.保存本用户的登录状态
//只有企业用户才有UserId
$user=User::where("userid",$res->UserId)->first();
Auth::login($user,true);
//跳转到首页
return redirect("/");
附录:
QyWeixinClass这个类的实现:
corpid = $config->corpid;
$this->txl_secret = $config->txl_secret;
$this->agent_id = $config->agent_id;
$this->agent_secret = $config->agent_secret;
$this->check_agent_id = $config->check_agent_id;
$this->check_agent_secret = $config->check_agent_secret;
//存储的目标路径
$this->prePath = realpath( storage_path( 'wx' ) ) . DIRECTORY_SEPARATOR;
}
/**
* 获取 Access Token, 基础函数
*/
public function getBaseAccessToken($secret) {
//TODO: access_token 应该全局存储与更新,以下代码以写入到文件中做示例
//TODO: 每个应用的access_token应独立存储,此处用secret作为区分应用的标识
$path = $this->prePath."$secret.php";
if ( file_exists( $path ) ) {
$data = json_decode( file_get_contents( $path ) );
}else {
$data = new \stdClass();
$data->expire_time=0;
}
if($data->expire_time < time()) {
$url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={$this->corpid}&corpsecret=$secret";
$res = json_decode($this->httpGet( $url ));
$access_token = $res->access_token;
if($access_token) {
$data->expire_time = time() + 7000;
$data->access_token = $access_token;
$fp = fopen( $path, "w" );
fwrite( $fp, json_encode( $data ) );
fclose( $fp );
}
} else {
$access_token = $data->access_token;
}
return $access_token;
}
/**
* 获取通讯录的 Access Token
*/
public function getAccessToken() {
return $this->getBaseAccessToken($this->txl_secret);
}
/**
* 获取应用的 Access Token
*
*/
public function getAgentAccessToken() {
return $this->getBaseAccessToken($this->agent_secret);
}
/**
* 获取OA打卡应用的 Access Token
*
*/
public function getOACheckAccessToken() {
return $this->getBaseAccessToken($this->check_agent_secret);
}
/*
* 获取 js Ticket
*/
public function getJsApiTicket() {
// jsapi_ticket 应该全局存储与更新
$path = $this->prePath."{$this->agent_id}_jsapi_ticket.php";
if ( file_exists( $path ) ) {
$data = json_decode( file_get_contents( $path ) );
}else {
$data = new \stdClass();
$data->expire_time=0;
}
if($data->expire_time < time()){
$app_access_token = $this->getAgentAccessToken();
$url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=$app_access_token";
$res = json_decode($this->httpGet( $url ));
$ticket = $res->ticket;
if ($ticket) {
$data->expire_time = time() + 7000;
$data->jsapi_ticket = $ticket;
$fp = fopen( $path, "w" );
fwrite( $fp, json_encode( $data ) );
fclose( $fp );
}
} else {
$ticket = $data->jsapi_ticket;
}
return $ticket;
}
/*
* 获取签名 jssdk
*/
public function getSignPackage() {
$jsapiTicket = $this->getJsApiTicket();
// 注意 URL 一定要动态获取,不能 hardcode.
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
$url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
$timestamp = time();
$nonceStr = $this->createNonceStr();
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr×tamp=$timestamp&url=$url";
$signature = sha1( $string );
$signPackage = array(
"appId" => $this->corpid,
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => $signature,
"rawString" => $string
);
return $signPackage;
}
/*
* 发送纯文本消息
* touser 成员ID列表(消息接收者,多个接收者用‘|’分隔,最多支持1000个)。特殊情况:指定为@all,则向该企业应用的全部成员发送
* toparty 部门ID列表,多个接收者用‘|’分隔,最多支持100个。当touser为@all时忽略本参数
* totag 标签ID列表,多个接收者用‘|’分隔,最多支持100个。当touser为@all时忽略本参数
* content 消息内容,最长不超过2048个字节,支持换行(转义过的\n)、A标签
*/
public function sendTextMsg($content, $touser, $toparty='', $totag='') {
$accessToken = $this->getAgentAccessToken();
//消息接口
$apiurl = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$accessToken";
//文本消息
$arr = [
"touser"=>$touser,
"toparty"=>$toparty,
"totag"=>$totag,
"msgtype"=>"text",
"agentid"=>$this->agent_id,
"text"=>[
"content"=>$content
]
];
$postdata = json_encode( $arr, JSON_UNESCAPED_UNICODE );
$res = $this->postCurl( $postdata, $apiurl );
return $res;
}
/*
* 发送文本卡片消息
* touser 成员ID列表(消息接收者,多个接收者用‘|’分隔,最多支持1000个)。特殊情况:指定为@all,则向关注该企业应用的全部成员发送
* toparty 部门ID列表,多个接收者用‘|’分隔,最多支持100个。当touser为@all时忽略本参数
* totag 标签ID列表,多个接收者用‘|’分隔,最多支持100个。当touser为@all时忽略本参数
* title 标题,不超过128个字节,超过会自动截断
* description 描述,不超过512个字节,支持使用br标签或者空格进行换行,支持div标签,内置了3种文字颜色:灰色(gray)、高亮(highlight)、默认黑色(normal),将其作为div标签的class属性即可;仅限企业微信里有效,企业号无效
* url 点击后跳转的链接。
*/
public function sendTextCardMsg( $title, $description, $url, $touser, $toparty='', $totag='' ) {
$accessToken = $this->getAgentAccessToken();
//消息接口
$apiurl = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$accessToken";
//文本消息
$arr = [
"touser"=>$touser,
"toparty"=>$toparty,
"totag"=>$totag,
"msgtype"=>"textcard",
"agentid"=>$this->agent_id,
"textcard"=> [
"title"=>$title,
"description"=>$description,
"url"=>$url
]
];
$postdata = json_encode( $arr, JSON_UNESCAPED_UNICODE );
$res = $this->postCurl( $postdata, $apiurl );
return $res;
}
/*
* 发送NEWS消息
* touser 成员ID列表(消息接收者,多个接收者用‘|’分隔,最多支持1000个)。特殊情况:指定为@all,则向关注该企业应用的全部成员发送
* toparty 部门ID列表,多个接收者用‘|’分隔,最多支持100个。当touser为@all时忽略本参数
* totag 标签ID列表,多个接收者用‘|’分隔,最多支持100个。当touser为@all时忽略本参数
* title 标题,不超过128个字节,超过会自动截断
* description 描述,不超过512个字节,超过会自动截断
* url 点击后跳转的链接。
* picurl 图文消息的图片链接,支持JPG、PNG格式,较好的效果为大图640x320,小图80x80。
*/
public function sendNewsMsg( $title, $description, $url, $picurl, $touser, $toparty='', $totag='' ) {
$accessToken = $this->getAgentAccessToken();
//消息接口
$apiurl = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$accessToken";
//文本消息
$arr = [
"touser"=>$touser,
"toparty"=>$toparty,
"totag"=>$totag,
"msgtype"=>"news",
"agentid"=>$this->agent_id,
"news"=> [
"articles"=>[
[
"title"=>$title,
"description"=>$description,
"url"=>$url,
"picurl"=>$picurl
]
]
]
];
$postdata = json_encode( $arr, JSON_UNESCAPED_UNICODE );
$res = $this->postCurl( $postdata, $apiurl );
return $res;
}
/*
* 随机字符串
*/
public function createNonceStr( $length = 16 ) {
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$str = "";
for ( $i = 0; $i < $length; $i++ ) {
$str .= substr( $chars, mt_rand( 0, strlen( $chars ) - 1 ), 1 );
}
return $str;
}
/**
* curl get
*/
public function httpGet( $url ) {
$oCurl = curl_init();
if(stripos($url,"https://")!==FALSE){
curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
}
curl_setopt($oCurl, CURLOPT_URL, $url);
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
//curl_setopt($oCurl, CURLOPT_VERBOSE, 1);
curl_setopt($oCurl, CURLOPT_HEADER, 0);
$sContent = curl_exec($oCurl);
// $aStatus = curl_getinfo($oCurl);
curl_close($oCurl);
return $sContent;
}
/**
* 以post方式提交到对应的接口url
*/
public function postCurl( $postdata, $url, $second = 30) {
$ch = curl_init();
if(stripos($url,"https://")!==FALSE){
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
}
//设置超时
curl_setopt( $ch, CURLOPT_TIMEOUT, $second );
curl_setopt( $ch, CURLOPT_URL, $url );
//设置header
curl_setopt( $ch, CURLOPT_HEADER, FALSE );
//要求结果为字符串且输出到屏幕上
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, TRUE );
//post提交方式
curl_setopt( $ch, CURLOPT_POST, TRUE );
curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata );
//运行curl
$data = curl_exec( $ch );;
//返回结果
if ( $data ) {
curl_close( $ch );
return $data;
} else {
$error = curl_errno( $ch );
curl_close( $ch );
throw new \Exception( "curl errno: $error" );
}
}
//测试日志
public function test($str){
$uploadDir = realpath(public_path());//物理路径
$filePath = $uploadDir . DIRECTORY_SEPARATOR . "schedule.txt";
$handle=fopen($filePath,"a+");
fwrite($handle, $str.'('.date('m-d H:i:s').') // '.PHP_EOL);
fclose($handle);
}
}