php 调用 wcf,C#-WCF框架-访问认证与接口安全-拦截器-ServiceStack.Redis-AccessToken-url请求过滤-限制越权访问-已登录-核心代码...

Git地址:待上传

TestServiceImpl.svc中的Service 需要对应 webconfig中的serviceActivations下service节点

redis windows端程序去网上下载,redis账号在web.config中配置,但是要注意ServiceStack.Redis库配置密码时无法连接redis服务

redis第三方库为ServiceStack.Redis

开启或关闭认证过滤或需要忽略认证的接口、资源、文件夹 均在App_Data/Config/Auth.config中配置

------------------------------------------------------------------------------

测试接口一览

http://localhost:23322/TestServiceImpl.svc/help

1、模拟登录

http://localhost:23322/TestServiceImpl.svc/Test/Login GET

{

"code": 0,

"message":"登录成功,看看能不能调用需要认证接口",

"result": {

"AccessToken":"5842735E-78C9-4235-833F-8029D489AA84",

"UserId":"88888"

}

}

2、需要登陆的接口需要添加头

http://localhost:23322/TestServiceImpl.svc/Test/Auth/1/5 GET

Content-Type application/json

accesstoken 5842735E-78C9-4235-833F-8029D489AA84

accessid 88888

{

"code": 0,

"message":"请求成功啦",

"result":"Param1=1,Param2=5"

}

3、会忽略登录认证的接口

http://localhost:23322/TestServiceImpl.svc/Test/IgnoreAuth GET

{

"code": 0,

"message":"请求成功啦",

"result":null

}

核心代码

--------------AuthInterceptor.cs

using com.jiangjiesheng.auth;

using com.jiangjiesheng.auth.Constant;

using System;

using System.Collections.Generic;

using System.Linq;

using System.ServiceModel;

using System.ServiceModel.Channels;

using System.ServiceModel.Dispatcher;

using System.Text.RegularExpressions;

using System.Web;

namespace com.jiangjiesheng.auth.Interceptors

{//IDispatchMessageInspector IClientMessageInspector

/**

*

* 20180429 拦截器

* 江节胜 dev@jiangjiesheng.cn

*

*/

public class AuthInterceptor : IDispatchMessageInspector

{

// 备份桌面的Weiz.Redis文件夹

String  AuthConfigPath = JSConfigTool.getAppDataConfigPath(JSConfigTool.DataConfigPath.AuthConfig);

public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)

{

bool isOpenAuth = Boolean.Parse(JSConfigTool.getAppDataConfig(AuthConfigPath,"authConfig/isOpenAuth"));

if (!isOpenAuth)//关闭验证

{

return true;

}

HttpRequest currentRequest = HttpContext.Current.Request;

String rawUrl = currentRequest.RawUrl;

if (!isSkipCheck(rawUrl))

{

//  var user = GetHeaderValue("keyval");

//  throw new Exception("未经授权的访问!"); 没能处理异常 但是这个是必须的 WCF_ExceptionHandler : IErrorHandler 添加后还是提示没有处理异常

// return ResponseStatus.getCode(Status.NEED_LOGIN);

// throw new FaultException(string.Format("Exception accessing database:{0}",

//"ceshi "), new FaultCode("Connect to database"));

//获取登录数据 然后 放行 ,如果实在获取不到action 方法名 ,就通过参数来数据

var accessToken = HttpContext.Current.Request.Headers["accesstoken"];

var accessId = HttpContext.Current.Request.Headers["accessid"];

if (String.IsNullOrWhiteSpace(accessToken) || String.IsNullOrWhiteSpace(accessId))

{

ServerResponser = ServerResponse.create(ResponseStatus.getCode(Status.ILLEGAL_REQUEST), ResponseStatus.getDesc(Status.ILLEGAL_REQUEST),"");

this.ReplyAndAbortRequest(r, channel, instanceContext);

return false;

}

ServerResponsequery = UserInfoCache.getUserInfo(accessId, accessToken);

bool isLogined = query.IsSuccess();

if (!isLogined)

{

ServerResponser = ServerResponse.create(ResponseStatus.getCode(Status.NEED_LOGIN), query.message,"");

this.ReplyAndAbortRequest(r, channel, instanceContext);

return false;

}

//刷新有效时间放在getUserInfo() 中了

return false;

}

return true;

}

public void BeforeSendReply(ref Message reply, object correlationState)// //AfterReceiveRequest 的返回值 将在BeforeSendReply 中 correlationState 上读取

{

Message returnV = reply;

Console.Write(reply.ToString());

}

private string GetHeaderValue(string name, string ns =)

{

var RequestMessage = OperationContext.Current.RequestContext.RequestMessage;

var headers = OperationContext.Current.IncomingMessageHeaders;

var index = headers.FindHeader(name, ns);

if (index > -1)

return headers.GetHeader(index);

else

return null;

}

private void ReplyAndAbortRequest(T t, IClientChannel channel, InstanceContext instanceContext)//

{

String res = JsonHelper.ObjectToJsonNotForSerialize(t);

HttpContext.Current.Response.AddHeader("Content-Type","application/json");//实测两个都可以

HttpContext.Current.Response.ContentType ="application/json";//实测两个都可以

HttpContext.Current.Response.Clear();

HttpContext.Current.Response.AddHeader("Content-Length","");//去掉Transfer-Encoding:chunked 头必须 但是某些情况导致请求不能中断

HttpContext.Current.Response.Write(JsonHelper.ObjectToJsonNotForSerialize(t));

HttpContext.Current.Response.Flush();

HttpContext.Current.Response.End();

HttpContext.Current.Response.Close();//这里处理结束后不要在BeforeSendReply()中继续调用

instanceContext.Abort();//起关键作用 中断

channel.Abort();

channel.Close();

HttpContext.Current.Request.Abort();

}

private bool isSkipCheck(String rawUrl)

{

if (string.IsNullOrEmpty(rawUrl))

{

return false;

}

String authIgnorePathStr = JSConfigTool.getAppDataConfig(AuthConfigPath,"authConfig/authIgnorePath");

authIgnorePathStr = authIgnorePathStr.Replace("\\r\\n","").Replace("\\\"","\"").Replace("\r\n","").Replace(" ","");

string[] arr = Regex.Split(authIgnorePathStr,"###", RegexOptions.IgnoreCase);

foreach (var urlin arr)

{

if (String.IsNullOrWhiteSpace(url))//最后一个是空

{

continue;

}

if (rawUrl.IndexOf(url) > -1)

{

return true;

}

}

return false;

}

}

}

--------RedisCacheHelper.cs

using ServiceStack.Redis;

using System;

using System.Collections.Generic;

using System.Configuration;

using System.Linq;

using System.Web;

namespace com.jiangjiesheng.auth

{

/**

*

* 20180429 redis (ServiceStack.Redis)

* 江节胜 dev@jiangjiesheng.cn

*

*/

public class RedisCacheHelper

{

private static readonly PooledRedisClientManager pool =null;

private static readonly string[] redisHosts =null;

public static int RedisMaxReadPool = int.Parse(ConfigurationManager.AppSettings["redis_max_read_pool"]);

public static int RedisMaxWritePool = int.Parse(ConfigurationManager.AppSettings["redis_max_write_pool"]);

static RedisCacheHelper()//static 会优先初始化,测试如果不通过的话使用单例模式写

{

var redisHostStr = ConfigurationManager.AppSettings["redis_server_session"];

if (!string.IsNullOrEmpty(redisHostStr))

{

redisHosts = redisHostStr.Split(',');

if (redisHosts.Length > 0)

{

//var client = new RedisClient("127.0.0.1", 6379);

pool =new PooledRedisClientManager(redisHosts, redisHosts,

new RedisClientManagerConfig()

{

MaxWritePoolSize = RedisMaxWritePool,

MaxReadPoolSize = RedisMaxReadPool,

AutoStart =true

});

}

}

}

public static Int32 Add(string key, T value, DateTime expiry)

{

if (value ==null)

{

return 0;

}

if (expiry <= DateTime.Now)

{

Remove(key);

return 0;

}

try

{

if (pool !=null)

{

using (var r = pool.GetClient())

{

if (r !=null)

{

r.SendTimeout = 1000;

r.Set(key, value, expiry - DateTime.Now);

}

}

}

}

catch (Exception ex)

{

string msg = string.Format("{0}:{1}发生异常!{2}","cache","存储", key);

throw ex;

return 0;

}

return 1;

}

public static void Add(string key, T value, TimeSpan slidingExpiration)

{

if (value ==null)

{

return;

}

if (slidingExpiration.TotalSeconds <= 0)

{

Remove(key);

return;

}

try

{

if (pool !=null)

{

using (var r = pool.GetClient())

{

if (r !=null)

{

r.SendTimeout = 1000;

r.Set(key, value, slidingExpiration);

}

}

}

}

catch (Exception ex)

{

string msg = string.Format("{0}:{1}发生异常!{2}","cache","存储", key);

throw ex;

}

}

public static T Get(string key)

{

if (string.IsNullOrEmpty(key))

{

return default(T);

}

T obj =default(T);

try

{

if (pool !=null)

{

using (var r = pool.GetClient())

{

if (r !=null)

{

r.SendTimeout = 1000;

obj = r.Get(key);

}

}

}

}

catch (Exception ex)

{

string msg = string.Format("{0}:{1}发生异常!{2}","cache","获取", key);

throw ex;

}

return obj;

}

public static void Remove(string key)

{

try

{

if (pool !=null)

{

using (var r = pool.GetClient())

{

if (r !=null)

{

r.SendTimeout = 1000;

r.Remove(key);

}

}

}

}

catch (Exception ex)

{

string msg = string.Format("{0}:{1}发生异常!{2}","cache","删除", key);

throw ex;

}

}

public static bool Exists(string key)

{

try

{

if (pool !=null)

{

using (var r = pool.GetClient())

{

if (r !=null)

{

r.SendTimeout = 1000;

return r.ContainsKey(key);

}

}

}

}

catch (Exception ex)

{

string msg = string.Format("{0}:{1}发生异常!{2}","cache","是否存在", key);

throw ex;

}

return false;

}

public static bool Expire(string key,TimeSpan expireIn)

{

try

{

if (pool !=null)

{

using (var r = pool.GetClient())

{

if (r !=null)

{

r.SendTimeout = 1000;

return r.ExpireEntryIn(key,expireIn);

}

}

}

}

catch (Exception ex)

{

string msg = string.Format("{0}:{1}发生异常!{2}","cache","是否存在", key);

throw ex;

}

return false;

}

}

}

--------UserInfoCache.cs

using com.jiangjiesheng.auth;

using com.jiangjiesheng.auth.Constant;

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

namespace com.jiangjiesheng.auth

{

/**

*

* 20180429 登录用户信息读取、保存、更新等

* 江节胜 dev@jiangjiesheng.cn

*

*/

public class UserInfoCache

{

public const Double userValidTime = 12;

static String AuthConfigPath = JSConfigTool.getAppDataConfigPath(JSConfigTool.DataConfigPath.AuthConfig);

///

/// 存值统一使用手机号(方便管理)

///

/// 特别注意:此服务不能用作SSO登录,因为校验了accesstoken,不需要,后期如果要做SSO,就要在客户端保存一个独立固定值key,相当于cookie值

///

/// 成功时 msg 中返回key

///

public static ServerResponsesetOrUpdateUserInfo(AppUserInfoEntity user)

{

bool isOpenAuth = Boolean.Parse(JSConfigTool.getAppDataConfig(AuthConfigPath,"authConfig/isOpenAuth"));

if (!isOpenAuth)//关闭验证

{

return ServerResponse.create(-1,"保存登陆信息失败:配置设置为不使用接口校验","");

}

String accessToken = (Keys.KEY_REDIS_USER_ACCESSTOKEN_PREFIX + System.Guid.NewGuid().ToString()).ToUpper();//accessToken

try

{

//存的时候使用openId取值

//取值的时候使用使用accessToken+(手机号或者openId)校验

if (user !=null && !String.IsNullOrWhiteSpace(user.UserId))

{

user.AccessToken = accessToken;//用于读取时校验accessToken和accessId是否匹配

String userJson = JsonHelper.ObjectToJsonNotForSerialize(user);

String accessId = (Keys.KEY_REDIS_USER_ACCESSID_PREFIX + user.UserId);// accessId

var nowT = DateTime.Now;//UtcNow

var expiredT = nowT.AddHours(userValidTime);

int result = RedisCacheHelper.Add(accessId, userJson, expiredT);

if (result == 1)

{

return ServerResponse.create(0,"保存登陆信息成功", accessToken);//成功的时候返回 reidsKey

}

return ServerResponse.create(-1,"保存登陆信息失败", accessToken);

}

return ServerResponse.create(-1,"保存登陆信息失败:数据异常", accessToken);

}

catch (Exception ex)

{

return ServerResponse.create(-1,"保存登陆信息失败:Redis服务异常,请联系管理员", accessToken);

}

}

public static ServerResponseupdateUserInfoExpireTime(String reidsKey, TimeSpan timeSpan)//TimeSpan.FromHours(userValidTime)

{

try

{

bool res = RedisCacheHelper.Expire(reidsKey, timeSpan);

if (res ==true)

{

return ServerResponse.create(0,"设置成功",null);

}

return ServerResponse.create(-1,"设置失败",null);

}

catch (Exception ex)

{

return ServerResponse.create(-1,"设置失败:Redis服务异常,请联系管理员",null);

}

}

///

///

///

/// 手机号或者openid都行

///

///

public static ServerResponsegetUserInfo(String accessId, String reidsKey)//reidsKey 外部提交

{

try

{

if (String.IsNullOrWhiteSpace(accessId) || String.IsNullOrWhiteSpace(reidsKey))

{

return ServerResponse.create(-1,"读取失败:无效请求",null);

}

String _accessId = (Keys.KEY_REDIS_USER_ACCESSID_PREFIX + accessId);// accessId

String userJson = RedisCacheHelper.Get(_accessId);//通过手机号取值

if (String.IsNullOrWhiteSpace(userJson))

{

return ServerResponse.create(-1,"用户未登录或登录失效",null);

}

userJson = userJson.Replace("\\r\\n","").Replace("\\\"","\"");

AppUserInfoEntity user = JsonHelper.JsonToObjectNotForSerialize(userJson);

if (String.Equals(user.UserId, accessId) && String.Equals(user.AccessToken, reidsKey))//redis保存和接口提交的做一次校验

{

//刷新有效时间

var nowT = DateTime.Now;//UtcNow

var expiredT = TimeSpan.FromHours(userValidTime);

UserInfoCache.updateUserInfoExpireTime(_accessId, expiredT);

return ServerResponse.create(0,"读取用户缓存信息成功", user);

}

return ServerResponse.create(-1,"读取失败:非法请求", user);

}

catch (Exception ex)

{

return ServerResponse.create(-1,"读取失败:Redis服务异常,请联系管理员",null);

}

}

}

}

-----------JSConfigTool.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Xml;

namespace com.jiangjiesheng.auth

{

/**

*

* 读取App_Data下自定义配置文件

*

* 20180125 江节胜 dev@jiangjiesheng.cn

*

*/

public class JSConfigTool

{

public enum DataConfigPath//枚举

{

WeChatOfficalAccount,//微信公众号

SMSSender,//短信发送,

AuthConfig//接口安全校验配置

}

public static String getAppDataConfigPath(DataConfigPath path)

{

switch (path)

{

case DataConfigPath.WeChatOfficalAccount:

return "~/App_Data/Config/WeiXin.config";

case DataConfigPath.SMSSender:

return "~/App_Data/Config/SMS.config";

case DataConfigPath.AuthConfig:

return "~/App_Data/Config/Auth.config";

default:

return null;

}

}

///

/// 20180125 江节胜 dev@jiangjiesheng.cn

///

/// 获取XML配置的信息

///

///  ~/App_Data/Config/WeiXin.config

///  weixin/appid (注意加上父节点)

public static String getAppDataConfig(String xmlConfigPath, String xmlNodeName)

{

XmlDocument Xml =new XmlDocument();

Xml.Load(HttpContext.Current.Server.MapPath(xmlConfigPath));

XmlNode xmlNode = Xml.SelectSingleNode(xmlNodeName);

if (xmlNode !=null)

{

return xmlNode.InnerText;

}

return null;

}

///

/// 20180125 江节胜 dev@jiangjiesheng.cn

///

/// 设置XML配置的信息

///

///  ~/App_Data/Config/WeiXin.config

///  weixin/appid (注意加上父节点)

///

///

public static Boolean setAppDataConfig(String xmlConfigPath, String xmlNodeName, String value)

{

XmlDocument Xml =new XmlDocument();

Xml.Load(HttpContext.Current.Server.MapPath(xmlConfigPath));

XmlNode xmlNode = Xml.SelectSingleNode(xmlNodeName);

if (xmlNode !=null)

{

xmlNode.InnerText = value;

Xml.Save(HttpContext.Current.Server.MapPath(xmlConfigPath));

return true;

}

return false;

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值