浅谈基于FormsAuthentication的认证
一般情况下,在我们做访问权限管理的时候,会把用户的正确登录后的基本信息保存在session中,以后用户每次请求页面或接口数据的时候,拿到
session中存储的用户基本信息,查看比较他有没有登录和能否访问当前页面。
session的原理,也就是在服务器端生成一个sessionid对应了存储的用户数据,而sessionid存储在cookie中,客户端以后每次请求都会带上这个
cookie,服务器端根据cookie中的sessionid找到存储在服务器端的对应当前用户的数据。
formsauthentication是微软提供给我们开发人员使用,做身份认证使用的。通过该认证,我们可以把用户name 和部分用户数据存储在cookie中,
通过基本的条件设置可以,很简单的实现基本的身份角色认证。
这里要实现的效果是:在不使用membership的情况下,使用系统提供的authorize 实现基于角色的访问控制。
1、创建认证信息 ticket
在用户登录以后,把用户的id和对应的角色(多个角色用,分隔),存储在ticket中。
使用formsauthentication.encrypt 加密票据。
把加密后的ticket 存储在response cookie中(客户端js不需要读取到这个cookie,所以最好设置httponly=true,防止浏览器攻击窃取、伪造cookie)。这样下次可以从request cookie中读取了。
一个简单的demo如下:
1 public actionresult login(string uname) 2 { 3 if (!string.isnullorempty(uname)) 4 { 5 //formsauthentication.setauthcookie(uname,true); 6 formsauthenticationticket ticket = new formsauthenticationticket 7 ( 1, 8 uname, 9 datetime.now, 10 datetime.now.addminutes(20), 11 true, 12 "7,1,8", 13 "/" 14 ); 15 var cookie = new httpcookie(formsauthentication.formscookiename,formsauthentication.encrypt(ticket)); 16 cookie.httponly = true; 17 httpcontext.response.cookies.add(cookie); 18 19 return redirecttoaction("userpage"); 20 } 21 return redirecttoaction("index"); 22 }
这里formsauthenticationticket 第六个参数存储的是string 类型的userdata ,这里就存放当前用户的角色id,以英文逗号分隔。
当使用用户名 “测试” 登录后,客户端就会出现这样一条记录cookie
2、获取认证信息
登录后,在内容页,我们可以通过,当前请求的user.identity.name 获取到uname信息,也可以通过读取request 中的cookie 解密,获取到ticket,再从其中获取uname 和 userdata (也就是之前存储的角色id信息)。
1 viewdata["user"]=user.identity.name; 2 3 var cookie = request.cookies[formsauthentication.formscookiename]; 4 var ticket = formsauthentication.decrypt(cookie.value); 5 string role = ticket.userdata; 6 7 viewdata["role"] = role; 8 return view();
3、通过注解属性,实现权限访问控制
在web.config中配置启用form认证 和 角色管理
1 <authentication mode="forms"> 2 <forms loginurl="~/login/index" timeout="2880" /> 3 </authentication> 4 <rolemanager enabled="true" defaultprovider="customroleprovid"> 5 <providers> 6 <clear/> 7 <add name="customroleprovid" type="mvcapp.helper.customroleprovider"/> 8 </providers> 9 </rolemanager>
当我们在controller 、action添加注解属性时候,设置的role是从哪里得到的呢?因为没有使用基于membership的那一套authentication,这里我们还要创建一个自定义的roleprovider 。名称为customroleprovider ,继承自 roleprovider。这里是在mvcapp下面的helper文件夹中创建了自己的customroleprovider.cs文件。
roleprovider中有很多abstract 方法,我们具体只实现其中的getrolesforuser 方法用于获取用户角色。这里的用户角色,我们可以根据拿到的用户id从数据库查询,或者拿取session中存储了的、或是cookie中存储了的。这里我前面已经把role存储在ticket的userdata中了,那就从ticket中获取吧。
1 public override string[] getrolesforuser(string username) 2 { 3 var cookie = httpcontext.current.request.cookies[formsauthentication.formscookiename]; 4 var ticket = formsauthentication.decrypt(cookie.value); 5 string role = ticket.userdata; 6 return role.split(','); 7 }
在需要,验证的controller、action上面添加注解属性,比如这个action 只允许roleid 为包含1或2或3的访问,而当前用户roleid为(7、1、8)就是用户有权访问了。
1 [authorize(roles="1,2,3")] 2 public actionresult role() 3 { 4 viewdata["user"] = user.identity.name; 5 return view(); 6 }
注:1、ticket存储在在cookie过期时间,和关闭浏览器是否在记住当前票据,在formsauthenticationticket实例化时候可以设置参数,
2、role 的获取可以不要存储在ticket 的userdata中,可以直接从数据库读取,userdata可以存储其他信息。
3、要想灵活配置controller 和action的 允许访问的role 可以自定义authorizeattribute override里面的onauthorization方法,在该方法中
读取当前页面允许访问的角色id,根据当前用户的roleid,进行检查。这样也就实现了,role的灵活配置。
4、ticket中的信息,最终还是存储在cookie中,安全性方面还是自己斟酌吧,个人觉得还是把userid和roleid存储在session中的比较好。