添加身份验证
play提供了一个模块-Secure(安全模块),用来做身份验证
允许Secure模块
修改yabe\conf\dependencies.yml,加入对secure的依赖
# Application dependencies require: - play -> crud - play -> secure
cmd命令行执行dependencies命令
E:\technology-hqh\proj\play-framework\yabe>play dependencies
cmd命令行执行eclipsify命令
E:\technology-hqh\proj\play-framework\yabe>play eclipsify
刷新工程,IDE中便可导入依赖包
修改yabe\conf\routes文件,为secure配置路由
# Routes # This file defines all application routes (Higher priority routes first) # ~~~~ # Home page GET / Application.index #import Secure routes * / module:secure # restful style route GET /post/{id} Application.show POST /post/{postId}/comments Application.postComment GET /captcha Application.captcha #import CRUD module * /admin module:crud # Ignore favicon requests GET /favicon.ico 404 # Map static resources from the /app/public folder to the /public path GET /public/ staticDir:public # Catch all * /{controller}/{action} {controller}.{action}
Secure需要的配置基本完成,访问主页仍然可以直接进入
这是因为还没有对任何Controller指定是否需要验证,所以还Secure还没开始工作!
为控制器添加身份验证
使用注解@With(Secure.class)标识Controller,Secure就会对访问该控制器进行身份验证
如,在Application类上加@With(Secure.class)
@With(Secure.class) public class Application extends Controller { ...... }
刷新页面,Secure开始工作了
随便输入用户名和密码都可以登录,即系统验证功能还没有真正开始
使用http://localhost:9000/logout 可以注销登录
定制系统身份认证
应用程序必须提供一个controllers.Secure.Security实例来定制身份认证处理。
通过继承这个类来创建我们自己版本的Secure类,可以指定如何对用户身份进行认证。
yabe\app\controllers下创建Security,重写authenticate()
package controllers; import models.User; public class Security extends Secure.Security { static boolean authenticate(String username, String password) { return User.login(username, password); } }
修改User类,增加username属性,以及一个以username和password为条件的查询的方法
package models; import java.util.ArrayList; import java.util.List; import javax.persistence.Entity; import javax.persistence.OneToMany; import play.data.validation.Email; import play.data.validation.Required; import play.db.jpa.Model; @Entity public class User extends Model { @Required public String username; @Email @Required public String email; @Required(message="input your pwd now!") public String password; public String fullname; public boolean isAdmin; //@OneToMany 声明User与Post之间是1对多的关系 //mappedBy="author" 表示将通过对方(User)的author字段来进行关联关系的维护 @OneToMany(mappedBy="author") public List<Post> posts; public User(String email,String password, String fullname) { this.email = email; this.password = password; this.fullname = fullname; this.posts = new ArrayList<Post>(0); } /** * 联合email和password两个条件查询User * @param email * @param password * @return */ public static User connect(String email, String password) { return find("byEmailAndPassword", email, password).first(); } /** * 登陆时根据username和password查询User * 如果存在,则允许登陆 * @param username * @param password * @return */ public static boolean login(String username, String password) { return find("byUsernameAndPassword", username, password).first() != null; } /** * 添加Post的动作放到User中,这样可以把Post设置到User的List<Post>集合中 * 这样实现了双方都持有对方的引用了 * @param title * @param content * @return */ public User addPost(String title, String content) { Post post = new Post(title,content,this).save(); this.posts.add(post); this.save(); return this; } @Override public String toString() { return "User [" + fullname + "]"; } }
修改yabe\conf\initial-data.yml ,为User对象加入username初始化值
注意,该文件对TAB键不友好,只认空格符作为间隔
# Test data User(bob): username: bob email: bob@gmail.com password: secret fullname: Bob isAdmin: true User(jeff): username: jeff email: jeff@gmail.com password: secret fullname: Jeff User(paul): username: paul email: paul@gmail.com password: secret fullname: Paul ...
http://localhost:9000/logout 注销,重新登陆
此时,只有输入正确的用户名和密码才能进入系统了
有效账户yml中的初始用户:[bob,secret] :[jeff,secret]:[paul,secret]
但是,对CRUD页面 http://localhost:9000/admin/ 控制不起作用!
集成CRUD管理域到博客中
超级用户可以管理所有的博客
普通用户可以管理自己的博客
首先,看一下用户登陆验证的内部执行逻辑
public class Secure extends Controller中的方法
/** * 登陆页面,点击登陆,将执行此方法 * username * password * checkbox框 --- remember */ public static void authenticate(@Required String username, String password, boolean remember) throws Throwable { // Check tokens Boolean allowed = false; try { // This is the deprecated method name // 该方法废弃,所以这里总会抛异常,进而执行catch块的代码 allowed = (Boolean)Security.invoke("authentify", username, password); } catch (UnsupportedOperationException e ) { // This is the official method name // 子类(class Security extends Secure.Security)复写了authenticate(),所以这里将调用我们自己的authenticate(),根据用户名和密码查询数据库 allowed = (Boolean)Security.invoke("authenticate", username, password); } if(validation.hasErrors() || !allowed) { flash.keep("url"); flash.error("secure.error"); params.flash(); login(); } // Mark user as connected // 如果登陆成功,session中存入登陆用户名 session.put("username", username); // Remember if needed // 如果需要保存登陆状态,则应勾选登陆页面的checkbox框 if(remember) { Date expiration = new Date(); String duration = "30d"; // maybe make this override-able expiration.setTime(expiration.getTime() + Time.parseDuration(duration)); response.setCookie("rememberme", Crypto.sign(username + "-" + expiration.getTime()) + "-" + username + "-" + expiration.getTime(), duration); } // Redirect to the original URL (or /) redirectToOriginalURL(); }
创建一个新的Controller,该控制器用来对CRUD管理界面进行控制
package controllers; import models.User; import play.mvc.Before; import play.mvc.Controller; public class Admin extends Controller { /** * 首先,用户登陆会被Security拦截,登陆成功会把username放入session中 * session.put("username",username); * 通过session.contains("username");判断用户是否已经登陆 * * @Before 访问Admin控制器时,将先执行由该注解标注的方法,进行拦截(过滤/检查) */ @Before static void setConnectedUser() { //Security.isConnected() 检查session中是否有username为key的map存在 //因为用户登陆后会用username作为key存储登陆信息 if(Security.isConnected()) { //Security.connected() 取得session中以username为key的value,即用户名 User user = User.find("byUsername", Security.connected()).first(); renderArgs.put("user", user.fullname); } } //返回管理CRUD功能模块的主页面 public static void index(){ render(); } }
为Admin控制器的index()添加模板
创建yabe\app\views\Admin\index.html
Welcome ${user}!
在主页面为Admin为CRUD功能模块加入超链接
更改Log in to write something的href属性,使其指向Admin的index()
<ul id="tools"> <li><a href="@{Admin.index()}">Log in to write something</a></li> </ul>
刷新页面
跳转到管理页面
当然,这里只是简单取了一下Admin.java中 @Before标注的setConnectedUser()方法所设置的用户名
到这里,完成了2个操作
一是Admin控制器中使用了拦截器,通过@Before进行设置
二是从Security.connected()中获得当前登陆的用户名,再使用renderArgs.put(key,value)将信息传递到页面中,以便进行显示当前登陆用户。
为CRUD模块管理页面配置一个模板
这里为admin/index.html配置一个父模板,对页面进行统一的设置(标题,附加信息,版权声明等)
<!DOCTYPE html> <html> <head> <title>Administration</title> <meta charset="utf-8"> <link rel="stylesheet" media="screen" href="@{'/public/stylesheets/main.css'}"> <link rel="shortcut icon" type="image/png" href="@{'/public/images/favicon.png'}"> <script src="@{'/public/javascripts/jquery-1.6.4.min.js'}" type="text/javascript"></script> <script src="@{'/public/javascripts/jquery.tools-1.2.5.toolbox.expose.min.js'}" type="text/javascript"></script> </head> <body id="admin"> <!-- 页面顶部显示的信息 --> <div id="header"> <div id="log">yabe. <span>administration</span></div> <ul id="tools"> <!-- 调用Secure的logout()进行注销 --> <li><a href="@{Secure.logout()}">Log out</a></li> </ul> </div> <!-- 子模板内容显示区 --> <div id="main"> #{doLayout /} </div> <!-- 页脚 --> <p id="footer"> Yabe is a (not that) powerful bolg engine built with the <a href="http://playframework.org">Play framework</a> as a tutorial application. </p> </body> </html>
刷新页面
可见模板已经开始生效了,点击logout,则登出
这里调用的是Secure内部的logout(),而且可以复写Secure中的其它方法
比如,登出之后需要进行某些操作,则复写onDisconnected()
比如,登录成功之后要进行某些操作,则复写onAuthenticated()
package controllers; import play.Logger; import models.User; public class Security extends Secure.Security { static boolean authenticate(String username, String password) { return User.login(username, password); } /** * 登陆成功后会调用onAuthenticated() */ static void onAuthenticated() { Logger.info(Secure.Security.connected()+"\tlogin"); //Admin.index(); //登陆成功后,自动跳转到管理页面 } /** * 注销后会调用onDisconnected() */ static void onDisconnected() { Logger.info(Secure.Security.connected()+"\tloginlogout"); Application.index(); } }
到此,CRUD管理页面尚未提供任何可操作的功能
现在,继续编辑模板,加入CRUD的超链接到管理页面
<!DOCTYPE html> <html> <head> <title>Administration</title> <meta charset="utf-8"> <link rel="stylesheet" media="screen" href="@{'/public/stylesheets/main.css'}"> <link rel="shortcut icon" type="image/png" href="@{'/public/images/favicon.png'}"> <script src="@{'/public/javascripts/jquery-1.6.4.min.js'}" type="text/javascript"></script> <script src="@{'/public/javascripts/jquery.tools-1.2.5.toolbox.expose.min.js'}" type="text/javascript"></script> </head> <body id="admin"> <!-- 页面顶部显示的信息 --> <div id="header"> <div id="log">yabe. <span>administration</span></div> <ul id="tools"> <!-- 调用Secure的logout()进行注销 --> <li><a href="@{Secure.logout()}">Log out</a></li> </ul> </div> <!-- 子模板内容显示区 --> <div id="main"> <ul id="adminMenu"> <!-- 如果登陆用户是超级管理员,则显示 --> <li class="${request.controller == 'Admin' ? 'selected' : ''}"> <a href="@{Admin.index()}">My Posts</a> </li> <!-- 使用Secure.check() 控制是否为超级用户,如果是,则显示下面的<li/> --> #{secure.check 'admin'} <li class="${request.controller == 'Posts' ? 'selected' : ''}"> <a href="@{Posts.list()}">Posts</a> </li> <li class="${request.controller == 'Comments' ? 'selected' : ''}"> <a href="@{Comments.list()}">Comments</a> </li> <li class="${request.controller == 'Users' ? 'selected' : ''}"> <a href="@{Users.list()}">Users</a> </li> <!-- 注意这里的结束标签是写在前面的! --> #{/secure.check} </ul> #{doLayout /} </div> <!-- 页脚 --> <p id="footer"> Yabe is a (not that) powerful bolg engine built with the <a href="http://playframework.org">Play framework</a> as a tutorial application. </p> </body> </html>
登出,使用一个isAdmin属性为false的账户(jeff或paul)进行登陆
打开管理页面,注意,当前是用paul进行登陆的,非管理员权限,但是其可以看到所有的博文
这是不行的!非管理员只应该看到自己的博文!
虽然admin.html模板中,已经使用了判断是否为'admin'属性了,但是,请注意,play默认的check()返回的是true。就好像做登陆那里一样,子类没有重写authenticate()时,不管用什么用户登陆都成功一样。
在Security控制器中覆盖Secure.Security的check()
package controllers; import play.Logger; import models.User; public class Security extends Secure.Security { /** * 覆盖Secure.Security中的authenticate() * 这样,play在进行登录验证时,就会调用到子类写的方法了 * @param username * @param password * @return */ static boolean authenticate(String username, String password) { return User.login(username, password); } /** * 登陆成功后会调用onAuthenticated() */ static void onAuthenticated() { Logger.info(Secure.Security.connected()+"\tlogin"); } /** * 注销后会调用onDisconnected() */ static void onDisconnected() { Logger.info(Secure.Security.connected()+"\tloginlogout"); Application.index(); } /** * 用户登陆成功后,继续对其操作权限进一步校验 * 如果User的isAdmin属性为true,则返回true,即其状态为'admin' * @param profile play将登陆用户的用户名传入 * @return */ static boolean check(String profile) { if("admin".equals(profile)) { return User.find("byUsername", Secure.Security.connected()).<User>first().isAdmin; } return false; } }
刷新页面,可以看到系统中所有实体对象的CRUD链接都没有呈现了,因为paul不是管理员
登出,使用bob进行登陆
由于bob的isAdmin属性为true,所以,他能看到所有的实体对象的CRUD链接
到这里,完成一半的工作了。
接下来考虑的是,普通用户登陆后,使用超管进行CRUD的链接直接操作资源
虽然他的权限不够导致页面无法呈现管理模块的链接,但他可以自己手动输入链接来访问资源
比如,paul不是超管,但是他知道管理博文的地址:http://localhost:9000/admin/posts
paul登陆后,手动链接到这个地址,一样可以进行CRUD操作
该如何是好?
第一,对CURD进行控制
当前,任何人都能通过http://localhost:9000/admin/访问CRUD管理对象
现在就对其进行控制
在每个实体对象对应的Controller上加入注解@With(Secure.class),表示访问该资源需要进行身份认证操作,如果认证失败,则由play自动跳转到index页面
package controllers; import play.mvc.With; @With(Secure.class) public class Posts extends CRUD { }
package controllers; import play.mvc.With; @With(Secure.class) public class Comments extends CRUD { }
package controllers; import play.mvc.With; @With(Secure.class) public class Users extends CRUD { }
现在,未登陆用户虽然能进入CRUD页面,但是无法进行对象的操作了!
第二,防止无超管权限的用户手工输入CRUD的管理链接进行非法操作
同样是通过注解来完成@check("admin"),检查当前用户是否具备超管权限
play考虑很周到,每个小功能都用一个注解来完成,很贴心!
package controllers; import play.mvc.With; //访问Post对象的列表,需要检查是是否登陆成功,没有,则返回登陆页面 @With(Secure.class) //操作Post对象,是否具有超管权限,没有,则提示Access Denied @Check("admin") public class Posts extends CRUD { }
Comments、Users进行同样的操作即可。
使用非管理员账号登陆,手动输入Post对象的管理链接http://localhost:9000/admin/posts
到此,未登陆用户不能访问CRUD页面,普通用户不能进行实体对象的CRUD操作!
修改CRUD模块的布局
为了让CRUD模块的布局与博客系统整体布局一致,需要重写其模板
首先要得到CRUD模板元素的布局方案
( play实际上就是将CRUD模块拷贝到了项目的views路径下:
Copied
E:\technology-hqh\soft\play-1.2.5\modules\crud\app/views/CRUD/layout.html
to
E:\technology-hqh\proj\play-framework\yabe\app/views/CRUD/layout.html
)
E:\technology-hqh\proj\play-framework\yabe>play crud:ov --layout
然后运行eclipsify命令,以便让IDE能够更新到新的内容
E:\technology-hqh\proj\play-framework\yabe>play eclipsify
此时,IDE的views目录下便多了一个目录-CRUD,其下有一个layout.html文件
修改这个文件,让其继承admin.html,并做一些调整
#{extends 'admin.html' /} #{set 'moreStyles'} <link rel="stylesheet" type="text/css" media="screen" href="@{'/public/stylesheets/crud.css'}" /> #{/set} <div id="crud"> #{if flash.success} <div class="crudFlash flashSuccess"> ${flash.success} </div> #{/if} #{if flash.error || error} <div class="crudFlash flashError"> ${error ?: flash.error} </div> #{/if} <div id="crudContent"> #{doLayout /} </div> </div>
刷新页面,此时CRUD页面的布局发生了变化,与系统的基本保持你一致了
修改登陆页面的样式
为登陆页面定制样式
cmd命令行:
E:\technology-hqh\proj\play-framework\yabe>play secure:ov --css
E:\technology-hqh\proj\play-framework\yabe>play eclipsify
刷新IDE
编辑yabe\public\stylesheets\secure.css
在其文本最上方加入一行
@import url(main.css);
默认登陆页面
刷新页面
继承了main.css,感觉没默认的漂亮哦~~~
修改登陆页面的文字
打开yabe\conf\messages,加入以下内容替换登陆页面的文字
secure.username=用户名: secure.password=密码: secure.remember=下次自动登陆 secure.signin=登陆
刷新页面
相关推荐
"yabe"这个文件名可能是项目的简称或别名,具体含义可能需要查看项目源代码才能明确。通常,一个Play项目会包含以下主要目录结构: 1. `app`:包含所有应用程序代码,如控制器、模型、视图和服务。 2. `conf`:存储...
在后续的教程中,我们将逐步学习如何在Yabe项目中添加数据验证、错误处理、权限管理、自动化测试、Web界面和国际化功能。Play框架提供了丰富的内置支持,使得开发者可以专注于业务逻辑,而不是基础设施的配置和管理...
综上所述,"yabe:另一个博客引擎 - Curso Play 1.3.1"涉及到使用Java语言和Play框架开发的一个博客引擎项目。通过学习这个课程或教程,开发者可以了解如何使用Play框架构建Web应用,并可能涉及数据库集成、模板引擎...
在CMD中运行`play new yabe`,然后进入项目目录并执行`play run`启动项目。在浏览器输入`http://localhost:9000`,如果看到项目启动成功,说明配置完成。若要将项目转换为Eclipse项目,可以使用`play eclipsify`命令...
创建一个新的Play应用,只需在命令行输入`play new yabe`,然后按照提示输入应用的全名,如"Yet Another Blog Engine"。这将生成一个名为yabe的目录,包含了标准的工程结构: 1. `App/`:包含应用程序的核心代码,...
矢部 另一个博客引擎(Play Framework 1.3.x)
同时,这两个工具也适用于实际项目中的设备调试和系统集成工作。安装文件"SetupYabe_v1.2.2.exe"是Yabe的安装程序,而"VTS_3.6.7.zip"则是VTS的压缩包,解压后即可运行。 总的来说,熟悉并掌握Yabe和VTS的使用,...
yabe:另一个后端库
在构建Web应用程序时,Play框架是一个非常流行的选择,特别是在Java开发者社区中。Play框架7版本教程中的"数据模型的首次迭代"部分主要讲解了如何在Play应用中设置数据模型,特别是利用Java Persistence API (JPA) ...
用C#编写的图形化资源管理器程序,用于浏览BACnet设备(在Windows和Linux上运行)。 当前同时支持BACnet IPv4,IPv6 + BACnet MSTP + BACnet PTP + BACnet以太网。 用于读取,写入,读取多个,写入多个,iam,whois...
一些常用的BACnet点位扫描工具包括Yabe,BACnetscan 等。 BACnet主站模拟工具:主站模拟工具用于模拟BACnet主站的功能,以便测试和验证设备的响应和行为。它们允许开发人员发送和接收BACnet命令和数据,并模拟不同的...
"yabe_sql" 可能是一个开源项目或工具的名字,而 "剧本1.3.x教程" 指的是该项目或工具的1.3.x版本的相关教学材料。"紧随其后" 暗示这是一个系列教程中的一个后续部分,可能是对之前版本的更新或者扩展。 【描述分析...
软件平台:stm32cubemx keil5 使用hal库生成基础代码,然后添BACnet mtsp部分,已经成功和电脑BACnet模拟软件Yabe通讯成功。 硬件平台:基于正点原子stm32f407探索者开发板硬件。
"矢部网上商城"是一个基于GitHub Pages部署的在线商城项目,其源代码托管在"yabe-online-mall.github.io"的仓库中。这个项目主要利用HTML(HyperText Markup Language)来构建网页结构,展示了如何使用基本的Web技术...
通过理解和研究这个源码,开发者可以更好地了解如何在实际项目中实现BACnet设备之间的通信,提高楼宇自动化的互操作性和效率。然而,由于BACnet协议的复杂性,初学者可能需要花费一定时间来熟悉其细节。希望这个源码...
Currently supports both BACnet IPv4, IPv6 + BACnet MSTP + BACnet PTP + BACnet Ethernet. Basic functions for read, write, read multiple, write multiple, iam, whois, subscribeCOV, notify, WriteFile, ...
"yetanotherbacnetexplorer-code-282-trunk"可能表示的是YABE项目的某个版本或分支。"code-282"可能是一个版本号,而"trunk"通常指的是主分支,意味着这是项目的主线开发代码。解压这个文件后,开发者将能看到源代码...
语言:English 更好的书签弹出窗口 功能:1.左键单击可在新选项卡中打开书签。 2.单击鼠标中键或Ctrl键并单击以在新的背景选项卡中打开书签。 3.通过打开另一个文件夹来关闭所有打开的文件夹。...