首页 文章 精选 留言 我的

精选列表

搜索[Java框架],共10000篇文章
优秀的个人博客,低调大师

如何用Flutter设计一个100%准确的埋点框架?

阿里妹导读:用户行为埋点是用来记录用户在操作时的一系列行为,也是业务做判断的核心数据依据,如果缺失或者不准确将会给业务带来不可恢复的损失。闲鱼将业务代码从Native迁移到Flutter上过程中,发现原先Native体系上的埋点方案无法应用在Flutter体系之上。而如果只把业务功能迁移过来就上线,是极其不负责任的。因此,经过不断探索,闲鱼技术团队沉淀了一套Flutter上的高准确率的用户行为埋点方案,今天由工程师兰昊来和大家分享一下。 用户行为埋点定位 先来讲讲在我们这里是如何定义用户行为埋点的。在如下用户时间轴上,用户进入A页面后,看到了按钮X,然后点击了这个按钮,随即打开了新的页面B。 这个时间轴上有如下5个埋点事件发生: 进入A页面。A页面首帧渲染完毕,并获得了焦点。 曝光坑位X。按钮X处于手机屏幕内,且停留一段时间,让用户可见可触

优秀的个人博客,低调大师

前后端分离架构使用shiro框架进行登录的两种实现

方法一:重写FormAuthenticationFilter 原理: 假设在shiro.xml中配置了 /** = authc 而默认authc对应org.apache.shiro.web.filter.authc.FormAuthenticationFilter过滤器 则表示所有路径都被此过滤器拦截 当未登录请求被拦截,会调用FormAuthenticationFilter.onAccessDeny(): 如果请求的是loginUrl,则调用AuthenticatingFilter.executeLogin() 如果不是,则使request重定向到loginUrl,并return false; AuthenticatingFilter.executeLogin(): 调用subject.login(token) 如果登录成功,则调用onLoginSuccess() 失败则调用onLoginFailure() AuthenticatingFilter.onLoginSuccess(): return true; AuthenticatingFilter.onLoginFailure(): return false; subject.login(token): 最终会调用realm的doGetAuthenticationInfo 思路 重写默认的FormAuthenticationFilter, 在onAccessDeny()方法中: 如果请求的是loginUrl,则调用AuthenticatingFilter.executeLogin() 如果不是,则返回json,提示“未登录,无法访问该地址” @Override protectedbooleanonAccessDenied(ServletRequestrequest,ServletResponseresponse)throwsException{ if(this.isLoginRequest(request,response)){ if(this.isLoginSubmission(request,response)){ if(log.isTraceEnabled()){ log.trace("Loginsubmissiondetected.Attemptingtoexecutelogin."); } returnthis.executeLogin(request,response); }else{ if(log.isTraceEnabled()){ log.trace("Loginpageview."); } returntrue; } }else{ if(log.isTraceEnabled()){ log.trace("Attemptingtoaccessapathwhichrequiresauthentication.ForwardingtotheAuthenticationurl["+this.getLoginUrl()+"]"); } response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); PrintWriterout=response.getWriter(); ServerResponseserverResponse=ServerResponse.createByErrorMessage("未登录,无法访问该地址"); Gsongson=GsonFactory.getGson(); Strings=gson.toJson(serverResponse); out.println(s); out.flush(); out.close(); returnfalse; } } 由于AuthenticatingFilter.executeLogin()会调用onLoginSuccess()和onLoginFailure()方法 所以我们重写两个方法,前者返回登录成功的json,并把当前用户放入session;后者返回登录失败的json @Override protectedbooleanonLoginSuccess(AuthenticationTokentoken,Subjectsubject,ServletRequestrequest,ServletResponseresponse)throwsException{ response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); PrintWriterout=null; try{ out=response.getWriter(); }catch(IOExceptione1){ e1.printStackTrace(); } HttpSessionsession=((HttpServletRequest)request).getSession(); Useruser=userMapper.selectByUserName(token.getPrincipal().toString()); session.setAttribute(Const.CURRENT_USER,user); ServerResponseserverResponse=ServerResponse.createBySuccessMsg("登录成功"); Gsongson=GsonFactory.getGson(); Strings=gson.toJson(serverResponse); out.println(s); out.flush(); out.close(); returntrue; } @Override protectedbooleanonLoginFailure(AuthenticationTokentoken,AuthenticationExceptione,ServletRequestrequest,ServletResponseresponse){ response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); PrintWriterout=null; try{ out=response.getWriter(); }catch(IOExceptione1){ e1.printStackTrace(); } ServerResponseserverResponse=ServerResponse.createByErrorMessage("登录失败"); Gsongson=GsonFactory.getGson(); Strings=gson.toJson(serverResponse); out.println(s); out.flush(); out.close(); returnfalse; } } 最后配置shiro.xml将authc对应的默认的FormAuthenticationFilter,替换成我们的MyAuthenticationFilter <beanid="shiroFilter"class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <propertyname="securityManager"ref="securityManager"/> <propertyname="loginUrl"value="/user/login.do"/> <propertyname="unauthorizedUrl"value="/user/unauthorized_err"/> <propertyname="filters"> <map> <entrykey="authc"value-ref="myAuthenticationFilter"/> </map> </property> <propertyname="filterChainDefinitions"> <value> /user/login_err.do=anon /user/unauthorized_err.do=anon /**=authc </value> </property> </bean> <beanid="myAuthenticationFilter"class="com.mmall.shiro.filter.MyAuthenticationFilter"/> 方法二:使用PassThruAuthenticationFilter代替FormAuthenticationFilter 原理: org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter源码: publicclassPassThruAuthenticationFilterextendsAuthenticationFilter{ publicPassThruAuthenticationFilter(){ } protectedbooleanonAccessDenied(ServletRequestrequest,ServletResponseresponse)throwsException{ if(this.isLoginRequest(request,response)){ returntrue; }else{ this.saveRequestAndRedirectToLogin(request,response); returnfalse; } } } 官方文档: An authentication filter that redirects the user to the login page when they are trying to access a protected resource. However, if the user is trying to access the login page, the filter lets the request pass through to the application code. The difference between this filter and the FormAuthenticationFilter is that on a login submission (by default an HTTP POST to the login URL), the FormAuthenticationFilter filter attempts to automatically authenticate the user by passing the username and password request parameter values to Subject.login(usernamePasswordToken) directly. Conversely, this controller always passes all requests to the loginUrl through, both GETs and POSTs. This is useful in cases where the developer wants to write their own login behavior, which should include a call to Subject.login(AuthenticationToken) at some point. For example, if the developer has their own custom MVC login controller or validator, this PassThruAuthenticationFilter may be appropriate. 我们继承PassThruAuthenticationFilter并重写onAccessDenied方法,和redirectToLogin方法 publicclassMyPassThruAuthenticationFilterextendsPassThruAuthenticationFilter{ privateStringloginErrUrl="/"; publicvoidsetLoginErrUrl(StringloginErrUrl){ this.loginErrUrl=loginErrUrl; } @Override protectedbooleanonAccessDenied(ServletRequestrequest,ServletResponseresponse)throwsException{ if(this.isLoginRequest(request,response)){ returntrue; }else{ this.saveRequestAndRedirectToLogin(request,response); returnfalse; } } //重写redirectToLogin方法是因为saveRequestAndRedirectToLogin方法会调用它,而原始的redirectToLogin方法会使得请求重定向到loginUrl @Override protectedvoidredirectToLogin(ServletRequestrequest,ServletResponseresponse)throwsIOException{ WebUtils.issueRedirect(request,response,loginErrUrl); } } 首先修改shiro.xml , 将authc默认对应的FormAuthenticationFilter修改为MyPassThruAuthenticationFilter <beanid="shiroFilter"class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <propertyname="securityManager"ref="securityManager"/> <propertyname="loginUrl"value="/user/login.do"/> <propertyname="unauthorizedUrl"value="/user/unauthorized_err"/> <propertyname="filters"> <map> <entrykey="authc"> <beanclass="com.mmall.shiro.filter.MyPassThruAuthenticationFilter"> <propertyname="loginErrUrl"value="/user/login_err.do"/><!--注意这里的loginErrUrl与上面的loginUrl的区别--> </bean> </entry> </map> </property> <propertyname="filterChainDefinitions"> <value> /=anon /user/login_err.do=anon /user/unauthorized_err.do=anon /user/logout.do=logout /**=authc </value> </property> </bean> 然后修改loginUrl对应的Contoller的方法,在其中要调用subject.login()完成shiro的认证 //UserController中:结合shiro,使用PassThruAuthenticationFilter的登录,需要调用subject.login()完成shiro的认证 @RequestMapping(value="login.do",method=RequestMethod.POST) @ResponseBody publicServerResponselogin(@RequestBodyUseruser,HttpSessionsession){ Subjectsubject=SecurityUtils.getSubject(); UsernamePasswordTokentoken=newUsernamePasswordToken(user.getUsername(),user.getPassword()); //ServerResponseserverResponse=iUserService.login(user.getUsername(),user.getPassword()); try{ /**subject.login(token)提交申请,验证能不能通过,也就是交给shiro。这里会回调reaml(或自定义的realm)里的一个方法 protectedAuthenticationInfodoGetAuthenticationInfo()*/ subject.login(token); }catch(AuthenticationExceptione){//验证身份失败 returnServerResponse.createByErrorMessage("登陆客户身份失败!"); } /**Shiro验证后,跳转到此处,这里判断验证是否通过*/ if(subject.isAuthenticated()){//验证身份通过 session.setAttribute(Const.CURRENT_USER,subject.getPrincipal()); returnServerResponse.createBySuccessMsg("登录成功"); }else{ returnServerResponse.createByErrorMessage("登陆客户身份失败!"); } } 而loginErrUrl对应的方法则返回Json提示未登录: @RequestMapping(value="login_err.do",method=RequestMethod.GET) @ResponseBody publicServerResponselogin_error(){ returnServerResponse.createByErrorMessage("用户未登录"); }

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册