2023年6月20日发(作者:)

javaweb权限表单设计_Web⽤户的⾝份验证及WebApi权限验证流程的设计和实现前⾔:Web ⽤户的⾝份验证,及页⾯操作权限验证是B/S系统的基础功能,⼀个功能复杂的业务应⽤系统,通过⾓⾊授权来控制⽤户访问,本⽂通过Form认证,Mvc的Controller基类及Action的权限验证来实现Web系统登录,Mvc前端权限校验以及WebApi服务端的访问校验功能。1. Web Form认证介绍Web应⽤的访问⽅式因为是基于浏览器的Http地址请求,所以需要验证⽤户⾝份的合法性。⽬前常见的⽅式是Form认证,其处理逻辑描述如下:1. ⽤户⾸先要在登录页⾯输⼊⽤户名和密码,然后登录系统,获取合法⾝份的票据,再执⾏后续业务处理操作;2. ⽤户在没有登录的情况下提交Http页⾯访问请求,如果该页⾯不允许匿名访问,则直接跳转到登录页⾯;3. 对于允许匿名访问的页⾯请求,系统不做权限验证,直接处理业务数据,并返回给前端;4. 对于不同权限要求的页⾯Action操作,系统需要校验⽤户⾓⾊,计算权限列表,如果请求操作在权限列表中,则正常访问,如果不在权限列表中,则提⽰“未授权的访问操作”到异常处理页⾯。2. WebApi 服务端Basic ⽅式验证WebApi服务端接收访问请求,需要做安全验证处理,验证处理步骤如下:1. 如果是合法的Http请求,在Http请求头中会有⽤户⾝份的票据信息,服务端会读取票据信息,并校验票据信息是否完整有效,如果满⾜校验要求,则进⾏业务数据的处理,并返回给请求发起⽅;2. 如果没有票据信息,或者票据信息不是合法的,则返回“未授权的访问”异常消息给前端,由前端处理此异常。3. 登录及权限验证流程流程处理步骤说明:1. ⽤户打开浏览器,并在地址栏中输⼊页⾯请求地址,提交;2. 浏览器解析Http请求,发送到Web服务器;Web服务器验证⽤户请求,⾸先判断是否有登录的票据信息;3. ⽤户没有登录票据信息,则跳转到登录页⾯;4. ⽤户输⼊⽤户名和密码信息;5. 浏览器提交登录表单数据给Web服务器;6. Web服务需要验证⽤户名和密码是否匹配,发送api请求给api服务器;7. api⽤户账户服务根据⽤户名,读取存储在数据库中的⽤户资料,判断密码是否匹配;1)如果⽤户名和密码不匹配,则提⽰密码错误等信息,然该⽤户重新填写登录资料;2)如果验证通过,则保存⽤户票据信息;8. 接第3步,如果⽤户有登录票据信息,则跳转到⽤户请求的页⾯;9. 验证⽤户对当前要操作的页⾯或页⾯元素是否有权限操作,⾸先需要发起api服务请求,获取⽤户的权限数据;10. api⽤户权限服务根据⽤户名,查找该⽤户的⾓⾊信息,并计算⽤户权限列表,封装为Json数据并返回;11. 当⽤户有权限操作页⾯或页⾯元素时,跳转到页⾯,并由页⾯Controller提交业务数据处理请求到api服务器;如果⽤户没有权限访问该页⾯或页⾯元素时,则显⽰“未授权的访问操作”,跳转到系统异常处理页⾯。12. api业务服务处理业务逻辑,并将结果以Json 数据返回;13. 返回渲染后的页⾯给浏览器前端,并呈现业务数据到页⾯;14. ⽤户填写业务数据,或者查找业务数据;15. 当填写或查找完业务数据后,⽤户提交表单数据;16. 浏览器脚本提交get,post等请求给web服务器,由web服务器再次解析请求操作,重复步骤2的后续流程;17. 当api服务器验证⽤户⾝份是,没有可信⽤户票据,系统提⽰“未授权的访问操作”,跳转到系统异常处理页⾯。4. Mvc前端代码⽰例4.1 ⽤户登录AccountControllerpublic class AccountController : Controller{//// GET: /Logon/public ActionResult Login(string returnUrl){Url = returnUrl;return View();}[HttpPost]public ActionResult Logon(LoginUser loginUser, string returnUrl){string strUserName = me;string strPassword = rd;var accountModel = new AccountModel();//验证⽤户是否是系统注册⽤户if (teUserLogin(strUserName, strPassword)){if (lUrl(returnUrl)){//创建⽤户ticket信息LoginUserTicket(strUserName, strPassword);//读取⽤户权限数据rAuthorities(strUserName);return new RedirectResult(returnUrl);}else{return RedirectToAction("Index", "Home");}}else{throw new ApplicationException("⽆效登录⽤户!");}}////// ⽤户注销,注销之前,清除⽤户ticket//////[HttpPost]public ActionResult Logout(){var accountModel = new AccountModel();();return RedirectToAction("Login", "Account");}}4.2 ⽤户模型AccountModelpublic class AccountModel{////// 创建登录⽤户的票据信息//////internal void CreateLoginUserTicket(string strUserName, string strPassword){//构造Form验证的票据信息FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, strUserName, ,utes(90),true, ("{0}:{1}", strUserName, strPassword), ookiePath);string ticString = t(ticket);//把票据信息写⼊Cookie和Session//SetAuthCookie⽅法⽤于标识⽤户的Identity状态为(new HttpCookie(ookieName, ticString));hCookie(strUserName, true);n["USER_LOGON_TICKET"] = ticString;//重写HttpContext中的⽤户⾝份,可以封装⾃定义⾓⾊数据;//判断是否合法⽤户,可以检查:enticated的属性值string[] roles = (',');IIdentity identity = new FormsIdentity(ticket);IPrincipal principal = new GenericPrincipal(identity, roles); = principal;}////// 获取⽤户权限列表数据/////////internal string GetUserAuthorities(string userName){//从WebApi 访问⽤户权限数据,然后写⼊Sessionstring jsonAuth = "[{"Controller": "SampleController", "Actions":"Apply,Process,Complete"}, {"Controller":"Product", "Actions": "List,Get,Detail"}]";var userAuthList = alizeFromString(jsonAuth, typeof(UserAuthModel[]));n["USER_AUTHORITIES"] = userAuthList;return jsonAuth;}////// 读取数据库⽤户表数据,判断⽤户密码是否匹配////////////internal bool ValidateUserLogin(string name, string password){//bool isValid = password == passwordInDatabase;return true;}////// ⽤户注销执⾏的操作///internal void Logout(){t();}}4.3 控制器基类WebControllerBase////// 前端Mvc控制器基类///[Authorize]public abstract class WebControllerBase : Controller{////// 对应api的Url///public string ApiUrl{get;protected set;}////// ⽤户权限列表///public UserAuthModel[] UserAuthList{get{return thList;}}////// 登录⽤户票据///public string UserLoginTicket{get{return ginTicket;}}}4.4 权限属性RequireAuthorizationAttribute////// 权限验证属性类///public class RequireAuthorizeAttribute : AuthorizeAttribute{////// ⽤户权限列表///public UserAuthModel[] UserAuthList{get{return thList;}}////// 登录⽤户票据///public string UserLoginTicket{get{return ginTicket;}}public override void OnAuthorization(AuthorizationContext filterContext){orization(filterContext);验证是否是登录⽤户var identity = ty;if (enticated){var actionName = Name;var controllerName = llerName;//验证⽤户操作是否在权限列表中if (HasActionQulification(actionName, controllerName, ))if (!OrEmpty(UserLoginTicket))//有效登录⽤户,有权限访问此Action,则写⼊Cookie信息s[ookieName].Value = UserLoginTicket;else//⽤户的Session, Cookie都过期,需要重新登录ct("~/Account/Login", false);else//虽然是登录⽤户,但没有该Action的权限,跳转到“未授权访问”页⾯ct("~/Home/UnAuthorized", true);}else{//未登录⽤户,则判断是否是匿名访问var attr = tomAttributes(true).OfType();bool isAnonymous = (a => a is AllowAnonymousAttribute);if (!isAnonymous)//未验证(登录)的⽤户, ⽽且是⾮匿名访问,则转向登录页⾯ct("~/Account/Login", true);}}////// 从权限列表验证⽤户是否有权访问Action////////////private bool HasActionQulification(string actionName, string controllerName, string userName){//从该⽤户的权限数据列表中查找是否有当前Controller和Action的itemvar auth = rDefault(a =>{bool rightAction = false;bool rightController = ller == controllerName;if (rightController){string[] actions = (',');rightAction = ns(actionName);}return rightAction;});//此处可以校验⽤户的其它权限条件//var notAllowed = HasOtherLimition(userName);//var result = (auth != null) && notAllowed;//return result;return (auth != null);}}4.5 业务Controller⽰例public class ProductController : WebControllerBase{[AllowAnonymous]public ActionResult Query(){return View("ProductQuery");}[HttpGet]//[AllowAnonymous][RequireAuthorize]public ActionResult Detail(string id){var cookie = s;string url = + "/Get/" + id;HttpClient httpClient = (url, ginTicket);string result = ing();var model = alizeFromString(result);ViewData["PRODUCT_ADD_OR_EDIT"] = "E";return View("ProductForm", model);}}5. WebApi 服务端代码⽰例5.1 控制器基类ApiControllerBase////// Controller的基类,⽤于实现适合业务场景的基础功能//////[BasicAuthentication]public abstract class ApiControllerBase : ApiController{}5.2 权限属性BaseAuthenticationAttribute////// 基本验证Attribtue,⽤以Action的权限处理///public class BasicAuthenticationAttribute : ActionFilterAttribute{////// 检查⽤户是否有该Action执⾏的操作权限//////public override void OnActionExecuting(HttpActionContext actionContext){//检验⽤户ticket信息,⽤户ticket信息来⾃调⽤发起⽅if (ization != null){//解密⽤户ticket,并校验⽤户名密码是否匹配var encryptTicket = ter;if (ValidateUserTicket(encryptTicket))onExecuting(actionContext);se = new HttpResponseMessage(orized);}else{//检查配置是否要求权限校验bool isRquired = (tings["WebApiAuthenticatedFlag"].ToString() == "true");if (isRquired){//如果请求Header不包含ticket,则判断是否是匿名调⽤var attr = tomAttributes().OfType();bool isAnonymous = (a => a is AllowAnonymousAttribute);//是匿名⽤户,则继续执⾏;⾮匿名⽤户,抛出“未授权访问”信息if (isAnonymous)onExecuting(actionContext);se = new HttpResponseMessage(orized);}else{onExecuting(actionContext);}}}////// 校验⽤户ticket信息/////////private bool ValidateUserTicket(string encryptTicket){var userTicket = t(encryptTicket);var userTicketData = ta;string userName = ing(0, f(":"));string password = ing(f(":") + 1);//检查⽤户名、密码是否正确,验证是合法⽤户//var isQuilified = CheckUser(userName, password);return true;}}5.3 api服务Controller实例public class ProductController : ApiControllerBase{[HttpGet]public object Find(string id){return (2);}// GET api/product/5[HttpGet][AllowAnonymous]public Product Get(string id){var headers = s;var p = d((id));if (p == null){throw new HttpResponseException(new HttpResponseMessage(uest)Content = new StringContent("id3 not found"), ReasonPhrase = "product id not exist." });}return p;}}6. 其它配置说明6.1 Mvc前端 配置machineKey节点配置,是应⽤于对⽤户ticket数据加密和解密。6.2 WebApi服务端配置machineKey节点配置,是应⽤于对⽤户ticket数据加密和解密。7. 总结Web系统的⽤户登录及页⾯操作权限验证在处理逻辑上⽐较复杂,需要考虑到Form认证、匿名访问,Session和Cookie存储,以及Session和Cookie的过期处理,本⽂实现了整个权限验证框架的基本功能,供系统架构设计⼈员以及Web开发⼈员参考。Demo项⽬代码地址:8. 春风吹⼜起--后记由于项⽬的需要,彻底打造了登录及权限验证的⼀个新开源项⽬,欢迎⼤家挪步。GitHub:博客⽂章在线DEMO地址:(⽤户名密码:admin/123456, jack/123456)---------------------

2023年6月20日发(作者:)

javaweb权限表单设计_Web⽤户的⾝份验证及WebApi权限验证流程的设计和实现前⾔:Web ⽤户的⾝份验证,及页⾯操作权限验证是B/S系统的基础功能,⼀个功能复杂的业务应⽤系统,通过⾓⾊授权来控制⽤户访问,本⽂通过Form认证,Mvc的Controller基类及Action的权限验证来实现Web系统登录,Mvc前端权限校验以及WebApi服务端的访问校验功能。1. Web Form认证介绍Web应⽤的访问⽅式因为是基于浏览器的Http地址请求,所以需要验证⽤户⾝份的合法性。⽬前常见的⽅式是Form认证,其处理逻辑描述如下:1. ⽤户⾸先要在登录页⾯输⼊⽤户名和密码,然后登录系统,获取合法⾝份的票据,再执⾏后续业务处理操作;2. ⽤户在没有登录的情况下提交Http页⾯访问请求,如果该页⾯不允许匿名访问,则直接跳转到登录页⾯;3. 对于允许匿名访问的页⾯请求,系统不做权限验证,直接处理业务数据,并返回给前端;4. 对于不同权限要求的页⾯Action操作,系统需要校验⽤户⾓⾊,计算权限列表,如果请求操作在权限列表中,则正常访问,如果不在权限列表中,则提⽰“未授权的访问操作”到异常处理页⾯。2. WebApi 服务端Basic ⽅式验证WebApi服务端接收访问请求,需要做安全验证处理,验证处理步骤如下:1. 如果是合法的Http请求,在Http请求头中会有⽤户⾝份的票据信息,服务端会读取票据信息,并校验票据信息是否完整有效,如果满⾜校验要求,则进⾏业务数据的处理,并返回给请求发起⽅;2. 如果没有票据信息,或者票据信息不是合法的,则返回“未授权的访问”异常消息给前端,由前端处理此异常。3. 登录及权限验证流程流程处理步骤说明:1. ⽤户打开浏览器,并在地址栏中输⼊页⾯请求地址,提交;2. 浏览器解析Http请求,发送到Web服务器;Web服务器验证⽤户请求,⾸先判断是否有登录的票据信息;3. ⽤户没有登录票据信息,则跳转到登录页⾯;4. ⽤户输⼊⽤户名和密码信息;5. 浏览器提交登录表单数据给Web服务器;6. Web服务需要验证⽤户名和密码是否匹配,发送api请求给api服务器;7. api⽤户账户服务根据⽤户名,读取存储在数据库中的⽤户资料,判断密码是否匹配;1)如果⽤户名和密码不匹配,则提⽰密码错误等信息,然该⽤户重新填写登录资料;2)如果验证通过,则保存⽤户票据信息;8. 接第3步,如果⽤户有登录票据信息,则跳转到⽤户请求的页⾯;9. 验证⽤户对当前要操作的页⾯或页⾯元素是否有权限操作,⾸先需要发起api服务请求,获取⽤户的权限数据;10. api⽤户权限服务根据⽤户名,查找该⽤户的⾓⾊信息,并计算⽤户权限列表,封装为Json数据并返回;11. 当⽤户有权限操作页⾯或页⾯元素时,跳转到页⾯,并由页⾯Controller提交业务数据处理请求到api服务器;如果⽤户没有权限访问该页⾯或页⾯元素时,则显⽰“未授权的访问操作”,跳转到系统异常处理页⾯。12. api业务服务处理业务逻辑,并将结果以Json 数据返回;13. 返回渲染后的页⾯给浏览器前端,并呈现业务数据到页⾯;14. ⽤户填写业务数据,或者查找业务数据;15. 当填写或查找完业务数据后,⽤户提交表单数据;16. 浏览器脚本提交get,post等请求给web服务器,由web服务器再次解析请求操作,重复步骤2的后续流程;17. 当api服务器验证⽤户⾝份是,没有可信⽤户票据,系统提⽰“未授权的访问操作”,跳转到系统异常处理页⾯。4. Mvc前端代码⽰例4.1 ⽤户登录AccountControllerpublic class AccountController : Controller{//// GET: /Logon/public ActionResult Login(string returnUrl){Url = returnUrl;return View();}[HttpPost]public ActionResult Logon(LoginUser loginUser, string returnUrl){string strUserName = me;string strPassword = rd;var accountModel = new AccountModel();//验证⽤户是否是系统注册⽤户if (teUserLogin(strUserName, strPassword)){if (lUrl(returnUrl)){//创建⽤户ticket信息LoginUserTicket(strUserName, strPassword);//读取⽤户权限数据rAuthorities(strUserName);return new RedirectResult(returnUrl);}else{return RedirectToAction("Index", "Home");}}else{throw new ApplicationException("⽆效登录⽤户!");}}////// ⽤户注销,注销之前,清除⽤户ticket//////[HttpPost]public ActionResult Logout(){var accountModel = new AccountModel();();return RedirectToAction("Login", "Account");}}4.2 ⽤户模型AccountModelpublic class AccountModel{////// 创建登录⽤户的票据信息//////internal void CreateLoginUserTicket(string strUserName, string strPassword){//构造Form验证的票据信息FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, strUserName, ,utes(90),true, ("{0}:{1}", strUserName, strPassword), ookiePath);string ticString = t(ticket);//把票据信息写⼊Cookie和Session//SetAuthCookie⽅法⽤于标识⽤户的Identity状态为(new HttpCookie(ookieName, ticString));hCookie(strUserName, true);n["USER_LOGON_TICKET"] = ticString;//重写HttpContext中的⽤户⾝份,可以封装⾃定义⾓⾊数据;//判断是否合法⽤户,可以检查:enticated的属性值string[] roles = (',');IIdentity identity = new FormsIdentity(ticket);IPrincipal principal = new GenericPrincipal(identity, roles); = principal;}////// 获取⽤户权限列表数据/////////internal string GetUserAuthorities(string userName){//从WebApi 访问⽤户权限数据,然后写⼊Sessionstring jsonAuth = "[{"Controller": "SampleController", "Actions":"Apply,Process,Complete"}, {"Controller":"Product", "Actions": "List,Get,Detail"}]";var userAuthList = alizeFromString(jsonAuth, typeof(UserAuthModel[]));n["USER_AUTHORITIES"] = userAuthList;return jsonAuth;}////// 读取数据库⽤户表数据,判断⽤户密码是否匹配////////////internal bool ValidateUserLogin(string name, string password){//bool isValid = password == passwordInDatabase;return true;}////// ⽤户注销执⾏的操作///internal void Logout(){t();}}4.3 控制器基类WebControllerBase////// 前端Mvc控制器基类///[Authorize]public abstract class WebControllerBase : Controller{////// 对应api的Url///public string ApiUrl{get;protected set;}////// ⽤户权限列表///public UserAuthModel[] UserAuthList{get{return thList;}}////// 登录⽤户票据///public string UserLoginTicket{get{return ginTicket;}}}4.4 权限属性RequireAuthorizationAttribute////// 权限验证属性类///public class RequireAuthorizeAttribute : AuthorizeAttribute{////// ⽤户权限列表///public UserAuthModel[] UserAuthList{get{return thList;}}////// 登录⽤户票据///public string UserLoginTicket{get{return ginTicket;}}public override void OnAuthorization(AuthorizationContext filterContext){orization(filterContext);验证是否是登录⽤户var identity = ty;if (enticated){var actionName = Name;var controllerName = llerName;//验证⽤户操作是否在权限列表中if (HasActionQulification(actionName, controllerName, ))if (!OrEmpty(UserLoginTicket))//有效登录⽤户,有权限访问此Action,则写⼊Cookie信息s[ookieName].Value = UserLoginTicket;else//⽤户的Session, Cookie都过期,需要重新登录ct("~/Account/Login", false);else//虽然是登录⽤户,但没有该Action的权限,跳转到“未授权访问”页⾯ct("~/Home/UnAuthorized", true);}else{//未登录⽤户,则判断是否是匿名访问var attr = tomAttributes(true).OfType();bool isAnonymous = (a => a is AllowAnonymousAttribute);if (!isAnonymous)//未验证(登录)的⽤户, ⽽且是⾮匿名访问,则转向登录页⾯ct("~/Account/Login", true);}}////// 从权限列表验证⽤户是否有权访问Action////////////private bool HasActionQulification(string actionName, string controllerName, string userName){//从该⽤户的权限数据列表中查找是否有当前Controller和Action的itemvar auth = rDefault(a =>{bool rightAction = false;bool rightController = ller == controllerName;if (rightController){string[] actions = (',');rightAction = ns(actionName);}return rightAction;});//此处可以校验⽤户的其它权限条件//var notAllowed = HasOtherLimition(userName);//var result = (auth != null) && notAllowed;//return result;return (auth != null);}}4.5 业务Controller⽰例public class ProductController : WebControllerBase{[AllowAnonymous]public ActionResult Query(){return View("ProductQuery");}[HttpGet]//[AllowAnonymous][RequireAuthorize]public ActionResult Detail(string id){var cookie = s;string url = + "/Get/" + id;HttpClient httpClient = (url, ginTicket);string result = ing();var model = alizeFromString(result);ViewData["PRODUCT_ADD_OR_EDIT"] = "E";return View("ProductForm", model);}}5. WebApi 服务端代码⽰例5.1 控制器基类ApiControllerBase////// Controller的基类,⽤于实现适合业务场景的基础功能//////[BasicAuthentication]public abstract class ApiControllerBase : ApiController{}5.2 权限属性BaseAuthenticationAttribute////// 基本验证Attribtue,⽤以Action的权限处理///public class BasicAuthenticationAttribute : ActionFilterAttribute{////// 检查⽤户是否有该Action执⾏的操作权限//////public override void OnActionExecuting(HttpActionContext actionContext){//检验⽤户ticket信息,⽤户ticket信息来⾃调⽤发起⽅if (ization != null){//解密⽤户ticket,并校验⽤户名密码是否匹配var encryptTicket = ter;if (ValidateUserTicket(encryptTicket))onExecuting(actionContext);se = new HttpResponseMessage(orized);}else{//检查配置是否要求权限校验bool isRquired = (tings["WebApiAuthenticatedFlag"].ToString() == "true");if (isRquired){//如果请求Header不包含ticket,则判断是否是匿名调⽤var attr = tomAttributes().OfType();bool isAnonymous = (a => a is AllowAnonymousAttribute);//是匿名⽤户,则继续执⾏;⾮匿名⽤户,抛出“未授权访问”信息if (isAnonymous)onExecuting(actionContext);se = new HttpResponseMessage(orized);}else{onExecuting(actionContext);}}}////// 校验⽤户ticket信息/////////private bool ValidateUserTicket(string encryptTicket){var userTicket = t(encryptTicket);var userTicketData = ta;string userName = ing(0, f(":"));string password = ing(f(":") + 1);//检查⽤户名、密码是否正确,验证是合法⽤户//var isQuilified = CheckUser(userName, password);return true;}}5.3 api服务Controller实例public class ProductController : ApiControllerBase{[HttpGet]public object Find(string id){return (2);}// GET api/product/5[HttpGet][AllowAnonymous]public Product Get(string id){var headers = s;var p = d((id));if (p == null){throw new HttpResponseException(new HttpResponseMessage(uest)Content = new StringContent("id3 not found"), ReasonPhrase = "product id not exist." });}return p;}}6. 其它配置说明6.1 Mvc前端 配置machineKey节点配置,是应⽤于对⽤户ticket数据加密和解密。6.2 WebApi服务端配置machineKey节点配置,是应⽤于对⽤户ticket数据加密和解密。7. 总结Web系统的⽤户登录及页⾯操作权限验证在处理逻辑上⽐较复杂,需要考虑到Form认证、匿名访问,Session和Cookie存储,以及Session和Cookie的过期处理,本⽂实现了整个权限验证框架的基本功能,供系统架构设计⼈员以及Web开发⼈员参考。Demo项⽬代码地址:8. 春风吹⼜起--后记由于项⽬的需要,彻底打造了登录及权限验证的⼀个新开源项⽬,欢迎⼤家挪步。GitHub:博客⽂章在线DEMO地址:(⽤户名密码:admin/123456, jack/123456)---------------------