• (八)play之yabe项目【身份验证】


    添加身份验证

    play提供了一个模块-Secure(安全模块),用来做身份验证

    允许Secure模块

    修改yabeconfdependencies.yml,加入对secure的依赖

    Html代码  收藏代码
    1. # Application dependencies  
    2.   
    3. require:  
    4.     - play -> crud  
    5.     - play -> secure  

    cmd命令行执行dependencies命令

    E: echnology-hqhprojplay-frameworkyabe>play dependencies

    cmd命令行执行eclipsify命令

    E: echnology-hqhprojplay-frameworkyabe>play eclipsify

    刷新工程,IDE中便可导入依赖包

    修改yabeconf outes文件,为secure配置路由

    Html代码  收藏代码
    1. # Routes  
    2. # This file defines all application routes (Higher priority routes first)  
    3. # ~~~~  
    4.   
    5. # Home page  
    6. GET     /                                       Application.index  
    7.   
    8. #import Secure routes  
    9. *       /                                       module:secure  
    10.   
    11. # restful style route  
    12. GET     /post/{id}                              Application.show  
    13.   
    14. POST    /post/{postId}/comments                 Application.postComment  
    15.   
    16. GET     /captcha                                Application.captcha  
    17.   
    18.   
    19. #import CRUD module  
    20. *       /admin                                  module:crud  
    21.   
    22.   
    23. # Ignore favicon requests  
    24. GET     /favicon.ico                            404  
    25.   
    26. # Map static resources from the /app/public folder to the /public path  
    27. GET     /public/                                staticDir:public  
    28.   
    29. # Catch all  
    30. *       /{controller}/{action}                  {controller}.{action}  

     Secure需要的配置基本完成,访问主页仍然可以直接进入

    这是因为还没有对任何Controller指定是否需要验证,所以还Secure还没开始工作!

     为控制器添加身份验证

    使用注解@With(Secure.class)标识Controller,Secure就会对访问该控制器进行身份验证

    如,在Application类上加@With(Secure.class)

    Java代码  收藏代码
    1. @With(Secure.class)  
    2. public class Application extends Controller {  
    3.   
    4.     ......  
    5.   
    6. }  

     刷新页面,Secure开始工作了



     

    随便输入用户名和密码都可以登录,即系统验证功能还没有真正开始

    使用http://localhost:9000/logout 可以注销登录

    定制系统身份认证

    应用程序必须提供一个controllers.Secure.Security实例来定制身份认证处理。

    通过继承这个类来创建我们自己版本的Secure类,可以指定如何对用户身份进行认证。
    yabeappcontrollers下创建Security,重写authenticate()

    Java代码  收藏代码
    1. package controllers;  
    2.   
    3. import models.User;  
    4.   
    5. public class Security extends Secure.Security {  
    6.       
    7.      static boolean authenticate(String username, String password) {  
    8.          return User.login(username, password);  
    9.      }  
    10. }  

     修改User类,增加username属性,以及一个以username和password为条件的查询的方法

    Java代码  收藏代码
    1. package models;  
    2.   
    3. import java.util.ArrayList;  
    4. import java.util.List;  
    5.   
    6. import javax.persistence.Entity;  
    7. import javax.persistence.OneToMany;  
    8.   
    9. import play.data.validation.Email;  
    10. import play.data.validation.Required;  
    11. import play.db.jpa.Model;  
    12.   
    13. @Entity  
    14. public class User extends Model {  
    15.       
    16.     @Required  
    17.     public String username;  
    18.       
    19.     @Email  
    20.     @Required  
    21.     public String email;  
    22.       
    23.     @Required(message="input your pwd now!")  
    24.     public String password;  
    25.       
    26.     public String fullname;  
    27.       
    28.     public boolean isAdmin;  
    29.       
    30.     //@OneToMany 声明User与Post之间是1对多的关系  
    31.     //mappedBy="author" 表示将通过对方(User)的author字段来进行关联关系的维护  
    32.     @OneToMany(mappedBy="author")  
    33.     public List<Post> posts;  
    34.       
    35.       
    36.     public User(String email,String password, String fullname) {  
    37.         this.email = email;  
    38.         this.password = password;  
    39.         this.fullname = fullname;  
    40.         this.posts = new ArrayList<Post>(0);  
    41.     }  
    42.       
    43.     /** 
    44.      * 联合email和password两个条件查询User 
    45.      * @param email 
    46.      * @param password 
    47.      * @return 
    48.      */  
    49.     public static User connect(String email, String password) {  
    50.         return find("byEmailAndPassword", email, password).first();  
    51.     }  
    52.       
    53.     /** 
    54.      * 登陆时根据username和password查询User 
    55.      * 如果存在,则允许登陆 
    56.      * @param username 
    57.      * @param password 
    58.      * @return 
    59.      */  
    60.     public static boolean login(String username, String password) {  
    61.         return find("byUsernameAndPassword", username, password).first() != null;  
    62.     }  
    63.       
    64.     /** 
    65.      * 添加Post的动作放到User中,这样可以把Post设置到User的List<Post>集合中 
    66.      * 这样实现了双方都持有对方的引用了 
    67.      * @param title 
    68.      * @param content 
    69.      * @return 
    70.      */  
    71.     public User addPost(String title, String content) {  
    72.         Post post = new Post(title,content,this).save();  
    73.         this.posts.add(post);  
    74.         this.save();  
    75.         return this;  
    76.     }  
    77.   
    78.     @Override  
    79.     public String toString() {  
    80.         return "User [" + fullname + "]";  
    81.     }  
    82.       
    83.       
    84.       
    85. }  

    修改yabeconfinitial-data.yml ,为User对象加入username初始化值

    注意,该文件对TAB键不友好,只认空格符作为间隔

    Html代码  收藏代码
    1. # Test data  
    2.   
    3. User(bob):  
    4.     username:       bob  
    5.     email:          bob@gmail.com  
    6.     password:       secret  
    7.     fullname:       Bob  
    8.     isAdmin:        true  
    9.       
    10. User(jeff):  
    11.     username:       jeff  
    12.     email:          jeff@gmail.com  
    13.     password:       secret  
    14.     fullname:       Jeff      
    15.       
    16. User(paul):  
    17.     username:       paul  
    18.     email:          paul@gmail.com  
    19.     password:       secret  
    20.     fullname:       Paul      
    21.   
    22. ...  

     http://localhost:9000/logout  注销,重新登陆

    此时,只有输入正确的用户名和密码才能进入系统了

    有效账户yml中的初始用户:[bob,secret] :[jeff,secret]:[paul,secret]

    但是,对CRUD页面 http://localhost:9000/admin/ 控制不起作用!



     

    集成CRUD管理域到博客中

    超级用户可以管理所有的博客

    普通用户可以管理自己的博客

    首先,看一下用户登陆验证的内部执行逻辑

    public class Secure extends Controller中的方法

    Java代码  收藏代码
    1. /** 
    2.  * 登陆页面,点击登陆,将执行此方法 
    3.  * username 
    4.  * password 
    5.  * checkbox框 --- remember 
    6.  */  
    7. public static void authenticate(@Required String username, String password, boolean remember) throws Throwable {  
    8.         // Check tokens  
    9.         Boolean allowed = false;  
    10.         try {  
    11.             // This is the deprecated method name  
    12.             // 该方法废弃,所以这里总会抛异常,进而执行catch块的代码  
    13.             allowed = (Boolean)Security.invoke("authentify", username, password);  
    14.         } catch (UnsupportedOperationException e ) {  
    15.             // This is the official method name  
    16.             // 子类(class Security extends Secure.Security)复写了authenticate(),所以这里将调用我们自己的authenticate(),根据用户名和密码查询数据库  
    17.             allowed = (Boolean)Security.invoke("authenticate", username, password);  
    18.         }  
    19.         if(validation.hasErrors() || !allowed) {  
    20.             flash.keep("url");  
    21.             flash.error("secure.error");  
    22.             params.flash();  
    23.             login();  
    24.         }  
    25.         // Mark user as connected  
    26.         // 如果登陆成功,session中存入登陆用户名  
    27.         session.put("username", username);  
    28.         // Remember if needed  
    29.         // 如果需要保存登陆状态,则应勾选登陆页面的checkbox框  
    30.         if(remember) {  
    31.             Date expiration = new Date();  
    32.             String duration = "30d";  // maybe make this override-able   
    33.             expiration.setTime(expiration.getTime() + Time.parseDuration(duration));  
    34.             response.setCookie("rememberme", Crypto.sign(username + "-" + expiration.getTime()) + "-" + username + "-" + expiration.getTime(), duration);  
    35.   
    36.         }  
    37.         // Redirect to the original URL (or /)  
    38.         redirectToOriginalURL();  
    39.     }  

    创建一个新的Controller,该控制器用来对CRUD管理界面进行控制

    Java代码  收藏代码
    1. package controllers;  
    2.   
    3. import models.User;  
    4. import play.mvc.Before;  
    5. import play.mvc.Controller;  
    6.   
    7. public class Admin extends Controller {  
    8.       
    9.     /** 
    10.      * 首先,用户登陆会被Security拦截,登陆成功会把username放入session中 
    11.      * session.put("username",username); 
    12.      * 通过session.contains("username");判断用户是否已经登陆 
    13.      *  
    14.      * @Before 访问Admin控制器时,将先执行由该注解标注的方法,进行拦截(过滤/检查) 
    15.      */  
    16.     @Before  
    17.     static void setConnectedUser() {  
    18.         //Security.isConnected() 检查session中是否有username为key的map存在  
    19.         //因为用户登陆后会用username作为key存储登陆信息  
    20.         if(Security.isConnected()) {  
    21.             //Security.connected() 取得session中以username为key的value,即用户名  
    22.             User user = User.find("byUsername", Security.connected()).first();  
    23.             renderArgs.put("user", user.fullname);  
    24.         }  
    25.     }  
    26.       
    27.     //返回管理CRUD功能模块的主页面  
    28.     public static void index(){  
    29.         render();  
    30.     }  
    31.       
    32.       
    33.       
    34. }  

     为Admin控制器的index()添加模板

    创建yabeappviewsAdminindex.html

    Html代码  收藏代码
    1. Welcome ${user}!  

    在主页面为Admin为CRUD功能模块加入超链接

    更改Log in to write something的href属性,使其指向Admin的index()

    Html代码  收藏代码
    1. <ul id="tools">  
    2.     <li><href="@{Admin.index()}">Log in to write something</a></li>  
    3. </ul>  

    刷新页面



     

     跳转到管理页面

    当然,这里只是简单取了一下Admin.java中 @Before标注的setConnectedUser()方法所设置的用户名

     到这里,完成了2个操作

    一是Admin控制器中使用了拦截器,通过@Before进行设置

    二是从Security.connected()中获得当前登陆的用户名,再使用renderArgs.put(key,value)将信息传递到页面中,以便进行显示当前登陆用户。

    为CRUD模块管理页面配置一个模板

    这里为admin/index.html配置一个父模板,对页面进行统一的设置(标题,附加信息,版权声明等)

    Html代码  收藏代码
    1. <!DOCTYPE html>  
    2.   
    3. <html>  
    4.     <head>  
    5.         <title>Administration</title>  
    6.         <meta charset="utf-8">  
    7.         <link rel="stylesheet" media="screen" href="@{'/public/stylesheets/main.css'}">  
    8.         <link rel="shortcut icon" type="image/png" href="@{'/public/images/favicon.png'}">  
    9.         <script src="@{'/public/javascripts/jquery-1.6.4.min.js'}" type="text/javascript"></script>  
    10.         <script src="@{'/public/javascripts/jquery.tools-1.2.5.toolbox.expose.min.js'}" type="text/javascript"></script>  
    11.     </head>  
    12.       
    13.     <body id="admin">  
    14.         <!-- 页面顶部显示的信息 -->  
    15.         <div id="header">  
    16.             <div id="log">yabe. <span>administration</span></div>  
    17.             <ul id="tools">  
    18.                 <!-- 调用Secure的logout()进行注销 -->  
    19.                 <li><href="@{Secure.logout()}">Log out</a></li>  
    20.             </ul>  
    21.         </div>  
    22.           
    23.         <!-- 子模板内容显示区 -->  
    24.         <div id="main">  
    25.             #{doLayout /}  
    26.         </div>  
    27.           
    28.         <!-- 页脚 -->  
    29.         <id="footer">  
    30.             Yabe is a (not that) powerful bolg engine built with the   
    31.             <href="http://playframework.org">Play framework</a> as a tutorial application.  
    32.         </p>  
    33.     </body>  
    34. </html>  

    刷新页面



     

    可见模板已经开始生效了,点击logout,则登出

    这里调用的是Secure内部的logout(),而且可以复写Secure中的其它方法

    比如,登出之后需要进行某些操作,则复写onDisconnected() 

    比如,登录成功之后要进行某些操作,则复写onAuthenticated() 

    Java代码  收藏代码
    1. package controllers;  
    2.   
    3. import play.Logger;  
    4. import models.User;  
    5.   
    6. public class Security extends Secure.Security {  
    7.        
    8.      static boolean authenticate(String username, String password) {  
    9.          return User.login(username, password);  
    10.      }  
    11.        
    12.      /** 
    13.       * 登陆成功后会调用onAuthenticated() 
    14.       */  
    15.      static void onAuthenticated() {  
    16.          Logger.info(Secure.Security.connected()+" login");  
    17.          //Admin.index();  
    18.                  //登陆成功后,自动跳转到管理页面  
    19.      }  
    20.        
    21.      /** 
    22.       * 注销后会调用onDisconnected() 
    23.       */  
    24.      static void onDisconnected() {  
    25.          Logger.info(Secure.Security.connected()+" loginlogout");  
    26.          Application.index();  
    27.      }  
    28.   
    29. }  

    到此,CRUD管理页面尚未提供任何可操作的功能

    现在,继续编辑模板,加入CRUD的超链接到管理页面

    Html代码  收藏代码
    1. <!DOCTYPE html>  
    2.   
    3. <html>  
    4.     <head>  
    5.         <title>Administration</title>  
    6.         <meta charset="utf-8">  
    7.         <link rel="stylesheet" media="screen" href="@{'/public/stylesheets/main.css'}">  
    8.         <link rel="shortcut icon" type="image/png" href="@{'/public/images/favicon.png'}">  
    9.         <script src="@{'/public/javascripts/jquery-1.6.4.min.js'}" type="text/javascript"></script>  
    10.         <script src="@{'/public/javascripts/jquery.tools-1.2.5.toolbox.expose.min.js'}" type="text/javascript"></script>  
    11.     </head>  
    12.       
    13.     <body id="admin">  
    14.         <!-- 页面顶部显示的信息 -->  
    15.         <div id="header">  
    16.             <div id="log">yabe. <span>administration</span></div>  
    17.             <ul id="tools">  
    18.                 <!-- 调用Secure的logout()进行注销 -->  
    19.                 <li><href="@{Secure.logout()}">Log out</a></li>  
    20.             </ul>  
    21.         </div>  
    22.           
    23.         <!-- 子模板内容显示区 -->  
    24.         <div id="main">  
    25.             <ul id="adminMenu">  
    26.                 <!-- 如果登陆用户是超级管理员,则显示 -->  
    27.                 <li class="${request.controller == 'Admin' ?  'selected' : ''}">  
    28.                     <href="@{Admin.index()}">My Posts</a>  
    29.                 </li>  
    30.                 <!-- 使用Secure.check() 控制是否为超级用户,如果是,则显示下面的<li/> -->  
    31.                 #{secure.check 'admin'}  
    32.                 <li class="${request.controller == 'Posts' ? 'selected' : ''}">  
    33.                     <href="@{Posts.list()}">Posts</a>  
    34.                 </li>  
    35.                 <li class="${request.controller == 'Comments' ? 'selected' : ''}">  
    36.                     <href="@{Comments.list()}">Comments</a>  
    37.                 </li>  
    38.                 <li class="${request.controller == 'Users' ? 'selected' : ''}">  
    39.                     <href="@{Users.list()}">Users</a>  
    40.                 </li>  
    41.                 <!-- 注意这里的结束标签是写在前面的! -->  
    42.                 #{/secure.check}  
    43.             </ul>  
    44.             #{doLayout /}  
    45.         </div>  
    46.       
    47.         <!-- 页脚 -->  
    48.         <id="footer">  
    49.             Yabe is a (not that) powerful bolg engine built with the   
    50.             <href="http://playframework.org">Play framework</a> as a tutorial application.  
    51.         </p>  
    52.     </body>  
    53. </html>  

    登出,使用一个isAdmin属性为false的账户(jeff或paul)进行登陆

    打开管理页面,注意,当前是用paul进行登陆的,非管理员权限,但是其可以看到所有的博文

    这是不行的!非管理员只应该看到自己的博文!



     

    虽然admin.html模板中,已经使用了判断是否为'admin'属性了,但是,请注意,play默认的check()返回的是true。就好像做登陆那里一样,子类没有重写authenticate()时,不管用什么用户登陆都成功一样。

    在Security控制器中覆盖Secure.Security的check()

    Java代码  收藏代码
    1. package controllers;  
    2.   
    3. import play.Logger;  
    4. import models.User;  
    5.   
    6. public class Security extends Secure.Security {  
    7.        
    8.      /** 
    9.       * 覆盖Secure.Security中的authenticate() 
    10.       * 这样,play在进行登录验证时,就会调用到子类写的方法了 
    11.       * @param username 
    12.       * @param password 
    13.       * @return 
    14.       */  
    15.      static boolean authenticate(String username, String password) {  
    16.          return User.login(username, password);  
    17.      }  
    18.        
    19.      /** 
    20.       * 登陆成功后会调用onAuthenticated() 
    21.       */  
    22.      static void onAuthenticated() {  
    23.          Logger.info(Secure.Security.connected()+" login");  
    24.      }  
    25.        
    26.      /** 
    27.       * 注销后会调用onDisconnected() 
    28.       */  
    29.      static void onDisconnected() {  
    30.          Logger.info(Secure.Security.connected()+" loginlogout");  
    31.          Application.index();  
    32.      }  
    33.        
    34.        
    35.      /** 
    36.       * 用户登陆成功后,继续对其操作权限进一步校验 
    37.       * 如果User的isAdmin属性为true,则返回true,即其状态为'admin' 
    38.       * @param profile  play将登陆用户的用户名传入 
    39.       * @return 
    40.       */  
    41.      static boolean check(String profile) {  
    42.          if("admin".equals(profile)) {  
    43.              return User.find("byUsername", Secure.Security.connected()).<User>first().isAdmin;  
    44.          }  
    45.          return false;  
    46.      }  
    47.   
    48. }  

     刷新页面,可以看到系统中所有实体对象的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页面

    Java代码  收藏代码
    1. package controllers;  
    2.   
    3. import play.mvc.With;  
    4.   
    5. @With(Secure.class)  
    6. public class Posts extends CRUD {  
    7.   
    8. }  
    Java代码  收藏代码
    1. package controllers;  
    2.   
    3. import play.mvc.With;  
    4.   
    5. @With(Secure.class)  
    6. public class Comments extends CRUD {  
    7.   
    8. }  
    Java代码  收藏代码
    1. package controllers;  
    2.   
    3. import play.mvc.With;  
    4.   
    5. @With(Secure.class)  
    6. public class Users extends CRUD {  
    7.   
    8. }  

    现在,未登陆用户虽然能进入CRUD页面,但是无法进行对象的操作了!

    第二,防止无超管权限的用户手工输入CRUD的管理链接进行非法操作

    同样是通过注解来完成@check("admin"),检查当前用户是否具备超管权限

    play考虑很周到,每个小功能都用一个注解来完成,很贴心!

    Java代码  收藏代码
    1. package controllers;  
    2.   
    3. import play.mvc.With;  
    4.   
    5. //访问Post对象的列表,需要检查是是否登陆成功,没有,则返回登陆页面  
    6. @With(Secure.class)  
    7.   
    8. //操作Post对象,是否具有超管权限,没有,则提示Access Denied  
    9. @Check("admin")  
    10. public class Posts extends CRUD {  
    11.   
    12. }  

     Comments、Users进行同样的操作即可。

    使用非管理员账号登陆,手动输入Post对象的管理链接http://localhost:9000/admin/posts

     

     

    到此,未登陆用户不能访问CRUD页面,普通用户不能进行实体对象的CRUD操作!

    修改CRUD模块的布局

    为了让CRUD模块的布局与博客系统整体布局一致,需要重写其模板

    首先要得到CRUD模板元素的布局方案

    (  play实际上就是将CRUD模块拷贝到了项目的views路径下:

       Copied

       E: echnology-hqhsoftplay-1.2.5modulescrudapp/views/CRUD/layout.html

       to

       E: echnology-hqhprojplay-frameworkyabeapp/views/CRUD/layout.html

    )

    E: echnology-hqhprojplay-frameworkyabe>play crud:ov --layout

    然后运行eclipsify命令,以便让IDE能够更新到新的内容

    E: echnology-hqhprojplay-frameworkyabe>play eclipsify

    此时,IDE的views目录下便多了一个目录-CRUD,其下有一个layout.html文件

    修改这个文件,让其继承admin.html,并做一些调整

    Html代码  收藏代码
    1. #{extends 'admin.html' /}  
    2. #{set 'moreStyles'}  
    3.     <link rel="stylesheet" type="text/css" media="screen" href="@{'/public/stylesheets/crud.css'}" />  
    4. #{/set}  
    5.   
    6. <div id="crud">  
    7.   
    8.     #{if flash.success}  
    9.         <div class="crudFlash flashSuccess">  
    10.             ${flash.success}  
    11.         </div>  
    12.     #{/if}  
    13.     #{if flash.error || error}  
    14.         <div class="crudFlash flashError">  
    15.             ${error ?: flash.error}  
    16.         </div>  
    17.     #{/if}  
    18.   
    19.     <div id="crudContent">  
    20.         #{doLayout /}  
    21.     </div>  
    22.   
    23. </div>  

    刷新页面,此时CRUD页面的布局发生了变化,与系统的基本保持你一致了



     

     

    修改登陆页面的样式

    为登陆页面定制样式

    cmd命令行:

    E: echnology-hqhprojplay-frameworkyabe>play secure:ov --css

    E: echnology-hqhprojplay-frameworkyabe>play eclipsify

    刷新IDE

    编辑yabepublicstylesheetssecure.css

    在其文本最上方加入一行

    Html代码  收藏代码
    1. @import url(main.css);   

    默认登陆页面



     

    刷新页面

    继承了main.css,感觉没默认的漂亮哦~~~

     

    修改登陆页面的文字

    打开yabeconfmessages,加入以下内容替换登陆页面的文字

    Html代码  收藏代码
    1. secure.username=用户名:   
    2.   
    3. secure.password=密码:   
    4.   
    5. secure.remember=下次自动登陆  
    6.   
    7. secure.signin=登陆  

    刷新页面



     

  • 相关阅读:
    centos下使用yum 安装pip
    unity3d Aniso Level 摄像机近地面清楚,远地面模糊
    Unity3D 多平台 预编译 宏定义
    Mecanim的Avater
    Mecanim 学习概述
    Unity物理系统的触发器
    Unity开发 资源准备
    我的Unity学习路线
    贴图在Unity中的设置
    在Unity中为模型使用表情
  • 原文地址:https://www.cnblogs.com/zhiji6/p/4445084.html
Copyright © 2020-2023  润新知