这里的对日志加密是指输入正确密码以后才能正常访问。
示例:
http://www.yibin001.com/Archives/6035.aspx
密码:123456
针对于BlogEngine来说,可以用它丰富的插件机制来实现。
首先明确,完成这个插件需要做哪些工作:
1、数据表结构的变化。要增加密码项,“密码”字段当然是少不了了。
2、表结构的变化势必会引起Post实体类属性的变化。
3、Post.Serving时触发事件,该事件负责针对性的改变e.Body
4、认证的实现。
这里我用的是SqlServer数据库,打开be_Posts表,执行以下语句
- ALTER TABLE dbo.be_Posts
- ADD AccessPassword VARCHAR(100) NOT NULL DEFAULT ''
- GO
ALTER TABLE dbo.be_PostsADD AccessPassword VARCHAR(100) NOT NULL DEFAULT ''GO
对应地在BlogEngine.Core.Post中增加属性AccessPassword
- private string _AccessPassword = string.Empty;
- public string AccessPassword
- {
- get { return _AccessPassword; }
- set {
- if (_AccessPassword != value) MarkChanged("AccessPassword");
- _AccessPassword = value;
- }
- }
private string _AccessPassword = string.Empty; public string AccessPassword { get { return _AccessPassword; } set { if (_AccessPassword != value) MarkChanged("AccessPassword"); _AccessPassword = value; } }
这里的AccessPassword不为空表明该日志需要密码才能访问。当然,这里还得修改相应的数据访问层代码,来初始化AccessPassword字段。这里略去。
下一步编写插件,针对Post.Serving事件编写,全部代码:
-
- [Extension("加密日志","1.0","yibin")]
- public class ZPostAccessExtension : System.Web.SessionState.IRequiresSessionState
- {
- string cookiekey = string.Empty;
- public ZPostAccessExtension()
- {
- Post.Serving += new EventHandler<ServingEventArgs>(Post_Serving);
- }
-
- void Post_Serving(object sender, ServingEventArgs e)
- {
- Post p = sender as Post;
-
- if (HttpContext.Current.User.IsInRole("administrators") || HttpContext.Current.User.Identity.Name == p.Author)
- return;
- cookiekey = "POSTACCESS_" + p.Id;
-
- if (HttpContext.Current.Session[cookiekey] != null)
- {
- return;
- }
- if (!string.IsNullOrEmpty(p.AccessPassword))
- {
- if (e.Location == ServingLocation.PostList)
- {
- e.Body = "加密日志,您无权查看";
- }
- else
- {
- e.Body = string.Format(@"<p class=""AccessPassword"">加密日志,请输入密码查阅</p><input type=""password"" name=""AccessPassword_{0}"" id=""AccessPassword_{0}"" class=""AccessPassword"" /><input type=""button"" value=""确定"" title=""确定"" onclick=""Check();""/>", p.PostIdentity);
- e.Body = e.Body.Replace("<", "<").Replace(">", ">");
- }
- }
- }
- }
[Extension("加密日志","1.0","yibin")]public class ZPostAccessExtension : System.Web.SessionState.IRequiresSessionState{ string cookiekey = string.Empty; public ZPostAccessExtension() { Post.Serving += new EventHandler<ServingEventArgs>(Post_Serving); } void Post_Serving(object sender, ServingEventArgs e) { Post p = sender as Post; if (HttpContext.Current.User.IsInRole("administrators") || HttpContext.Current.User.Identity.Name == p.Author) return; cookiekey = "POSTACCESS_" + p.Id; //这里很关键,用Session来判断当前用户没有权限访问,如果指定的session存在,则直接退出,否则更改e.Body值。 if (HttpContext.Current.Session[cookiekey] != null) { return; } if (!string.IsNullOrEmpty(p.AccessPassword)) { if (e.Location == ServingLocation.PostList) { e.Body = "加密日志,您无权查看"; } else { e.Body = string.Format(@"<p class=""AccessPassword"">加密日志,请输入密码查阅</p><input type=""password"" name=""AccessPassword_{0}"" id=""AccessPassword_{0}"" class=""AccessPassword"" /><input type=""button"" value=""确定"" title=""确定"" onclick=""Check();""/>", p.PostIdentity); e.Body = e.Body.Replace("<", "<").Replace(">", ">"); //这里要对已经HtmlEncode后的字符进行还原,否则显示不正确。 } } }}
接下来就是实现认证这一步,我采用的是实现ICallbackEventHandler接口来进行无刷新操作。
先看很简单的js代码
- <script type="text/javascript">
- var accessMessage = document.getElementById('AccessMessage');
- function Check()
- {
- accessMessage.style.display='block';
- var pwd = '';
- var panel = document.getElementById('AccessPassword_<%=Post.PostIdentity %>');
- if(panel) {
- pwd = panel.value;
- }
- if(pwd==''){
- accessMessage.innerHTML='密码不能为空';
- panel.focus();
- return false;
- }
- accessMessage.innerHTML='正在验证......';
- <%= Page.ClientScript.GetCallbackEventReference(this,"pwd","checkCallback","")%>;
- }
-
- function checkCallback(rValue) {
- if(rValue=='error')
- {
- accessMessage.style.display='block';
- accessMessage.innerHTML='密码认证失败';
- setTimeout(function(){accessMessage.style.display='none';},2000);
- }
- else{
- accessMessage.style.display='block';
- accessMessage.innerHTML='验证成功';
- setTimeout(function(){document.getElementById('entrybody_<%=Post.PostIdentity %>').innerHTML = rValue;},500);
- setTimeout(function(){accessMessage.style.display='none';},3000);
- }
- }
- function ServerError(error) {
- alert(error);
- }
- </script>
<script type="text/javascript">var accessMessage = document.getElementById('AccessMessage');function Check() { accessMessage.style.display='block'; var pwd = ''; var panel = document.getElementById('AccessPassword_<%=Post.PostIdentity %>'); //DOM找到密码输入框 if(panel) { pwd = panel.value; } if(pwd==''){ accessMessage.innerHTML='密码不能为空'; panel.focus(); return false; } accessMessage.innerHTML='正在验证......'; <%= Page.ClientScript.GetCallbackEventReference(this,"pwd","checkCallback","")%>; } function checkCallback(rValue) { if(rValue=='error') { accessMessage.style.display='block'; accessMessage.innerHTML='密码认证失败'; setTimeout(function(){accessMessage.style.display='none';},2000); } else{ accessMessage.style.display='block'; accessMessage.innerHTML='验证成功'; setTimeout(function(){document.getElementById('entrybody_<%=Post.PostIdentity %>').innerHTML = rValue;},500); setTimeout(function(){accessMessage.style.display='none';},3000); } } function ServerError(error) { alert(error); } </script>
.cs文件中的关于ICallbackEventHandler接口的实现
- #region ICallbackEventHandler 成员
- protected string _callback = string.Empty;
- string ICallbackEventHandler.GetCallbackResult()
- {
- return _callback;
- }
-
- void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument)
- {
- if (Post.AccessPassword == eventArgument.Trim())
- {
- string SessionKey = "POSTACCESS_" + Post.Id;
- HttpContext.Current.Session.Add(SessionKey, true);
- string path = Utils.RelativeWebRoot + "themes/" + BlogSettings.Instance.Theme + "/PostView.ascx";
-
- PostViewBase postView = (PostViewBase)LoadControl(path);
- postView.Post = Post;
- postView.Location = ServingLocation.SinglePost;
- _callback = postView.Body;
-
- HttpContext.Current.Session.Remove(SessionKey);
- }
- else
- {
- _callback = "error";
- }
- }
- #endregion
#region ICallbackEventHandler 成员 protected string _callback = string.Empty; string ICallbackEventHandler.GetCallbackResult() { return _callback; } void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument) { if (Post.AccessPassword == eventArgument.Trim()) { string SessionKey = "POSTACCESS_" + Post.Id; HttpContext.Current.Session.Add(SessionKey, true); //注意这里,当密码验证成功后写session string path = Utils.RelativeWebRoot + "themes/" + BlogSettings.Instance.Theme + "/PostView.ascx"; PostViewBase postView = (PostViewBase)LoadControl(path); postView.Post = Post; postView.Location = ServingLocation.SinglePost; _callback = postView.Body; //此处仍然会执行Post.Serving 事件,但因为前面已经写入了Session,所以触发Post.Serving 事件时将直接返回日志内容。 //因为我想每次访问日志都需要输密码,所以,当正确返回日志内容后,清除掉该Session HttpContext.Current.Session.Remove(SessionKey); } else { _callback = "error"; } } #endregion
OK,整个过程就是如此,很是简单吧。
同样的原理,可以实现类似于论坛的“回复后可见”同样的功能,如果想长时间维持日志的正常查看权,可将Session换成Cookie。
另外,BlogEngine默认在web.config中将enableSessionState设为了false,请一定要设为true,否则无法使用Session!
我就是没有认真看web.config,在Session的获取上大费周折。