<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 基于 Glassfish 表單的身份驗證示例 > 原文: [https://javatutorial.net/glassfish-form-based-authentication-example](https://javatutorial.net/glassfish-form-based-authentication-example) 在本教程中,我將向您展示如何使用內置的 Glassfish 身份驗證機制通過用戶登錄創建基于 Web 的應用程序。 ![](https://img.kancloud.cn/6f/b2/6fb253e075f360cd5603206d31b2fd68_800x424.jpg) 如今,身份驗證已廣泛用于所有類型的 Web 應用程序中。 從網上商店,低谷社交媒體和要求用戶登錄的其他服務。Glassfish 帶有集成的身份驗證和授權機制,可讓用戶登錄并維護應用程序中的會話。 我們將利用此功能并構建和應用程序,以允許用戶注冊和登錄以訪問授權的內容。 最后,我將向您展示如何創建注銷功能以允許用戶終止會話。 這是一個完整的 Java EE 教程,它要求 DB 設置,Glassfish 配置,身份驗證后端邏輯的開發以及創建前端部分。 我將使用 Glassfish 4,MySQL 數據庫和 JSF 2.2 來構建前端。 該代碼也可以在 Payara 4 Server 和其他數據庫(MSSQL,Oracle 等)上運行,而無需進行任何修改。 ## 項目描述 我們將創建一個簡單的 Web 應用程序,其中包含一個注冊表單,一個登錄表單和一個私人頁面。 用戶登錄后即可訪問該私人頁面。 表單中的所有字段都將根據錯誤的用戶輸入進行驗證。 ## 創建數據庫表 在繼續之前,請確保已使用數據庫配置了 Glassfish 服務器。 如果尚未執行此操作,請先閱讀[如何使用 MySQL 配置 Glassfish 4](https://javatutorial.net/configure-glassfish-mysql)。 當然,您可以使用您喜歡的任何其他數據庫來學習本教程。 您不僅限于 MySQL。 您可以在多個位置存儲用戶憑據,例如數據庫,文件,LDAP 等。在本教程中,我們將使用數據庫存儲用戶名和密碼。 在 Glassfish 中,這稱為 JDBC 安全領域。 創建安全領域需要兩張表 - 第一個用于存儲用戶憑據,第二個用于將特定用戶映射到角色。 Glassfish 并未預定義角色,這意味著我們可以創建自己的角色 - 管理員,用戶,主持人,查看者等。為了使本教程盡可能簡短,我將只實現一個角色:用戶 第一個表稱為`users`,具有以下字段: * `email` – 唯一的主要字段。 我們將使用用戶的電子郵件地址作為用戶名,如果您愿意,可以使用簡單的舊用戶名 * `password` – SHA-256 編碼的用戶密碼。 切勿將用戶密碼存儲為純文本格式 - 不好?? * `name` – 用戶名 Glassfishe 的安全領域只有用戶名和密碼字段為必填項。 我在此處添加了名稱,以說明您可以在同一表單中為用戶添加其他信息 ```java CREATE TABLE `users` ( `email` varchar(255) NOT NULL, `password` varchar(64) NOT NULL, `name` varchar(30) NOT NULL, PRIMARY KEY (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ``` `user_groups`表具有 2 個必填字段: * `email` - 這是用戶名 * `groupname` – 存儲用戶的角色,例如管理員,用戶,主持人,查看者等。 ```java CREATE TABLE `user_groups` ( `email` VARCHAR(255) NOT NULL, `groupname` VARCHAR(32) NOT NULL, PRIMARY KEY (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ``` ## 項目結構 在開始實現項目的后端部分之前,我想向您展示項目結構。 這樣,您將知道所有文件所在的位置 ![](https://img.kancloud.cn/5a/a6/5aa6130c91934b9c5797b02c83a2cb92_375x576.jpg) 您可以在 GitHub 上找到完整的源代碼,網址為 [https://github.com/JavaTutorialNetwork/Tutorials/tree/master/GlassfishFormBasedAuthentication](https://github.com/JavaTutorialNetwork/Tutorials/tree/master/GlassfishFormBasedAuthentication) ## 項目依賴關系(`pom.xml`文件) 這是純 Java EE 7 的實現,除了 Java 企業 API 本身之外,我們在項目中不需要其他依賴項。 該 API 已包含在 Glassfish 中,因此您應將依賴項標記為`provided` ```java <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.javatutorial.tutorials</groupId> <artifactId>GlassfishFormBasedAuthentication</artifactId> <version>1</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <finalName>authexample</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.3</version> <configuration> <webXml>src/main/webapp/WEB-INF/web.xml</webXml> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project> ``` ## 用戶和組實體 在數據庫中添加表`users`和`user_groups`后,必須為這兩個表創建 JPA 實體。 實體會將數據庫表映射到 Java 對象,因此您可以使用實體對象中的 getter 和 setter 方法輕松地修改或獲取數據庫。 使用`@Entity`注解將 Java 類標記為實體。 該類還必須實現`Serializable`接口。 這是**用戶實體**的代碼 ```java package net.javatutorial.tutorials.gfauthexample.entity; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; @Entity @NamedQueries({ @NamedQuery(name = "findUserById", query = "SELECT u FROM User u WHERE u.email = :email") }) @Table(name="users") public class User implements Serializable { private static final long serialVersionUID = -5892169641074303723L; @Id @Column(name="email", nullable=false, length=255) private String email; @Column(name="password", nullable=false, length=64) private String password; @Column(name="name", nullable=false, length=30) private String name; public User(){} public User(String email, String password, String name) { this.email = email; this.password = password; this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ``` 在第 14 行,定義了`NamedQuery "findUserById"`。 我們稍后將在項目中使用此查詢來: * 驗證注冊期間提供的電子郵件是否尚未用于其他注冊 * 登錄后顯示用戶名和電子郵件 在第 11 行,用戶實體通過`@Table`注釋映射到數據庫表`users` 在第 18 行,`@Id`注釋用于將“電子郵件”表示為主字段 `@Column`注解用于將字段從 Java 類映射到表中的字段 **團體實體** ```java package net.javatutorial.tutorials.gfauthexample.entity; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="user_groups") public class Group implements Serializable { private static final long serialVersionUID = 1528447384986169065L; public static final String USERS_GROUP = "users"; @Id @Column(name="email", nullable=false, length=255) private String email; @Column(name="groupname", nullable=false, length=32) private String groupname; public Group() {} public Group(String email, String groupname) { this.email = email; this.groupname = groupname; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getGroupname() { return groupname; } public void setGroupname(String groupname) { this.groupname = groupname; } } ``` 字段`groupname`用于給定用戶角色。 該表是一個映射表,用戶 ID 映射到某個角色。 為了使此示例簡單,在本教程中創建的所有帳戶都將具有“用戶”角色。 您可以根據需要添加更多角色。 角色不是預定義的,您可以隨意命名。 角色之間的區別在于實現。 例如,如果您具有“管理員”角色,則將要賦予它比“用戶”更多的特權。 您必須以編程方式限制一個角色,并以編程方式為另一個角色提供更多選擇,并在您自己的代碼中進行管理。 ## `persistence.xml`文件 ```java <?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0"> <persistence-unit name="tutorialsPU" transaction-type="JTA"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <jta-data-source>jdbc/tutorialsDS</jta-data-source> <class>net.javatutorial.tutorials.gfauthexample.entity.User</class> <class>net.javatutorial.tutorials.gfauthexample.entity.Group</class> <properties> <!-- tables will be created only if they do not exist. Use for production <property name="eclipselink.ddl-generation" value="create-tables"/> --> <!-- will first drop the existing table, and then create the new table. Use for development <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/> --> <property name="eclipselink.logging.level" value="INFO"/> </properties> </persistence-unit> </persistence> ``` 在此文件中,我們指定持久層屬性。 首先,我們創建一個命名的持久性單元`tutorialsPU`。 我們將在 EJB 中使用此持久性單元為`EntityManager`創建隔離的`PersistenceContext`。 其次,我們指定將用于此持久性單元的數據源。 數據源在 Glassfish 管理控制臺中配置(有關更多詳細信息,請閱讀[如何使用 MySQL 配置 Glassfish 4](https://javatutorial.net/configure-glassfish-mysql))。 第三,我們列出要包含在持久性單元中的實體類。 在我們的例子中,這是我們在上一步中創建的`User`和`Group`類。 我還包括了兩個其他有用的屬性。 您可以根據需要將其注釋掉: 如果數據庫表不存在,此屬性將生成數據庫表 ```java <property name="eclipselink.ddl-generation" value="create-tables"/> ``` 部署應用程序時,此屬性將擦除現有表,并根據您的實體類創建新的(空)表。 您可以將其用于開發 ```java <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/> ``` ## 編寫業務層 – `UserEJB` 在這一步中,我們將編寫一個 Enterprise Java Bean(EJB),以在數據庫中插入新用戶并對其進行查詢。 EJB 通常用作持久層(數據庫實體)和表示層(托管 Bean 和 JSF 頁面)之間的中介。 ```java package net.javatutorial.tutorials.gfauthexample.ejb; import java.util.logging.Level; import java.util.logging.Logger; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; import net.javatutorial.tutorials.gfauthexample.entity.Group; import net.javatutorial.tutorials.gfauthexample.entity.User; import net.javatutorial.tutorials.gfauthexample.utils.AuthenticationUtils; @Stateless public class UserEJB { @PersistenceContext(unitName="tutorialsPU") private EntityManager em; public User createUser(User user) { try { user.setPassword(AuthenticationUtils.encodeSHA256(user.getPassword())); } catch (Exception e) { Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e); e.printStackTrace(); } Group group = new Group(); group.setEmail(user.getEmail()); group.setGroupname(Group.USERS_GROUP); em.persist(user); em.persist(group); return user; } public User findUserById(String id) { TypedQuery<User> query = em.createNamedQuery("findUserById", User.class); query.setParameter("email", id); User user = null; try { user = query.getSingleResult(); } catch (Exception e) { // getSingleResult throws NoResultException in case there is no user in DB // ignore exception and return NULL for user instead } return user; } } ``` 請注意,第 16 行的 Bean 被注釋為`@Stateless`。 在第 19 行,`EntityManager`指向`"tutorialsPU"`作為持久性單元,全部通過`@PersistenceContext`注解完成。 我們在`persistence.xml`文件中指定了持久性單元 `createUser`方法首先將純文本密碼編碼為 SHA-256 字符串,然后將兩個對象(用戶和組)存儲到數據庫中。 如果在數據庫中找不到該用戶,則`findUserById`方法將返回一個`User`對象或`null` 請注意此處`try-catch`的用法。 如果未找到具有給定 ID(電子郵件)的用戶,則對`query.getSingleResult()`的調用將引發`NoResultException`。 我們將忽略該異常,而是返回一個`null`用戶。 稍后,我們將在托管 Bean 中處理`null`。 實用程序將密碼編碼為 SHA-256 的方法 ```java package net.javatutorial.tutorials.gfauthexample.utils; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import javax.xml.bind.DatatypeConverter; public final class AuthenticationUtils { /** * Returns SHA-256 encoded string * @param password - the string to be encoded * @return SHA-256 encoded string * @throws UnsupportedEncodingException if UTF-8 is not supported by the system * @throws NoSuchAlgorithmException if SHA-256 is not supported by the system */ public static String encodeSHA256(String password) throws UnsupportedEncodingException, NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(password.getBytes("UTF-8")); byte[] digest = md.digest(); return DatatypeConverter.printBase64Binary(digest).toString(); } } ``` ## 配置 Glassfish 安全領域 我們需要在 Glassfish 中創建和配置一個新的 JDBC 安全領域。 在`localhost:4848`打開 Glassfish 管理控制臺。 打開“配置 -&gt; 服務器配置 -&gt; 安全 -&gt; 領域”,然后單擊“新建…”按鈕以創建新的安全領域。 ![Glassfish server configuration security realms](https://img.kancloud.cn/38/db/38db67882256541893d2226dfa1609f9_982x858.jpg) Glassfish 服務器配置安全領域 ![Create new JDBC security realm](https://img.kancloud.cn/44/ae/44ae791fe62239e2f71c331325174eb8_1024x778.jpg) 創建新的 JDBC 安全領域 填寫以下字段: * `Name ` – `jdbc-realm` * `Class Name` – `com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm` * `JAAS Context` – `jdbcRealm` * `JNDI` – `jdbc/tutorialsDS` * `User Table` – 用戶 * `User Name Column` – 電子郵件 * `Password Column` - 密碼 * `Group Table` – user_groups * `Group Table User Name Column` – 電子郵件 * `Group Name Column` – 組名 * `Password Encryption Algorithm` - 無(我們通過編程方式對密碼進行加密) * `Digest Algorithm` – SHA-256 * `Encoding` – Base64 ## `glassfish-web.xml`文件 ```java <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd"> <glassfish-web-app error-url=""> <security-role-mapping> <role-name>users</role-name> <group-name>users</group-name> </security-role-mapping> <class-loader delegate="true" /> <jsp-config> <property name="keepgenerated" value="true"> <description>Keep a copy of the generated servlet class' java code.</description> </property> </jsp-config> <parameter-encoding default-charset="UTF-8" /> </glassfish-web-app> ``` 我們必須在`glassfish-web.xml`文件中指定`security-role-mapping`,并添加角色名稱和組名稱 ## `web.xml`文件 ```java <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>Form Based Auth Example</display-name> <context-param> <param-name>javax.faces.PROJECT_STAGE</param-name> <param-value>Development</param-value> </context-param> <context-param> <param-name>javax.faces.DEFAULT_SUFFIX</param-name> <param-value>.xhtml</param-value> </context-param> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.xhtml</url-pattern> </servlet-mapping> <session-config> <session-timeout> 300 </session-timeout> </session-config> <welcome-file-list> <welcome-file>signin.xhtml</welcome-file> </welcome-file-list> <!-- SECURITY --> <login-config> <auth-method>FORM</auth-method> <realm-name>jdbc-realm</realm-name> <form-login-config> <form-login-page>/signin.xhtml</form-login-page> <form-error-page>/signin.xhtml</form-error-page> </form-login-config> </login-config> <security-role> <description/> <role-name>users</role-name> </security-role> <security-constraint> <display-name>Restricted to users</display-name> <web-resource-collection> <web-resource-name>Restricted Access</web-resource-name> <url-pattern>/user/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>users</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>NONE</transport-guarantee> </user-data-constraint> </security-constraint> </web-app> ``` `web.xml`部署描述符也需要進行一些更改。 我們將在此處配置安全規則。 首先,我們配置會話超時。 這是使用戶會話保持活動狀態的時間。 當會話超時時,用戶不必再次登錄。 該值以分鐘為單位。 300 表示用戶會話將持續 5 個小時。 其次,我們將`welcome-file`設置為`signin.xhtml`。 當用戶打開應用程序時,將顯示登錄屏幕。 在`<login-config>`中設置以下內容: * `auth-method`是`FORM` – 用戶使用 Web 表單輸入用戶名和密碼 * `realm-name`是`jdbc-realm` – 這是我們在上一步中配置的安全領域的名稱 * `form-login-page`和`form-error-page`是帶有登錄表單的 xhtml 文件和在發生登錄錯誤時顯示的 xhtml 文件(我們都指向`signin.xhtml`,因為在此文件中進行了錯誤處理) 我們只有一個安全角色–用戶 在`<security-constraints>`標簽中,我們為特定用戶角色定義了受限資源。 換句話說 – `user`目錄中的每個文件都需要登錄才能訪問。 我們將在其中放置私有 XHTML 文件。 ## 創建展示層 ![Registration form](https://img.kancloud.cn/d7/b1/d7b15dfe0e265f839d2839827145a0b6_329x242.jpg) 注冊表單 ### 注冊表單頁面 在注冊表單中,我們要求提供用戶名,電子郵件地址和密碼。 我們還要求用戶確認密碼。 我們將驗證電子郵件,密碼和確認密碼字段中的用戶輸入。 首先讓我們看一下 JSF XHTML 文件和`ManagedBean` ```java <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head> <title>register</title> </h:head> <h:body> <h:form> <!-- register a PostValidateEvent --> <f:event listener="#{registerView.validatePassword}" type="postValidate" /> <h3>Create new account</h3> <h:panelGrid columns="2"> <h:outputLabel for="name">Name:</h:outputLabel> <h:inputText id="name" value="#{registerView.name}" required="true" requiredMessage="Please enter your name" maxlength="30"></h:inputText> <h:outputLabel for="email">E-Mail:</h:outputLabel> <h:inputText id="email" value="#{registerView.email}" required="true" requiredMessage="Please enter your e-mail address"> <f:validator validatorId="emailValidator" /> </h:inputText> <h:outputLabel for="password">Password:</h:outputLabel> <h:inputSecret id="password" value="#{registerView.password}" required="true" requiredMessage="Please enter password" validatorMessage="Password must contain atleast one lowercase character, uppercase character, a digit and it's length must be between 6 and 20 characters"> <f:validateRegex pattern="((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,20})" /> </h:inputSecret> <h:outputLabel for="confirmpassword">Confirm password:</h:outputLabel> <h:inputSecret id="confirmpassword" value="#{registerView.confirmPassword}" required="true" requiredMessage="Please confirm your password"></h:inputSecret> <h:commandButton action="#{registerView.register}" value="Register"></h:commandButton> </h:panelGrid> </h:form> <br /> <br /> <h:link value="I already have an account" outcome="signin" /> </h:body> </html> ``` 注冊視圖 ```java package net.javatutorial.tutorials.gfauthexample.managedbeans; import java.io.Serializable; import java.util.logging.Logger; import javax.faces.application.FacesMessage; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import javax.faces.component.UIComponent; import javax.faces.component.UIInput; import javax.faces.context.FacesContext; import javax.faces.event.ComponentSystemEvent; import javax.inject.Inject; import net.javatutorial.tutorials.gfauthexample.ejb.UserEJB; import net.javatutorial.tutorials.gfauthexample.entity.User; @ManagedBean @SessionScoped public class RegisterView implements Serializable { private static final long serialVersionUID = 1685823449195612778L; private static Logger log = Logger.getLogger(RegisterView.class.getName()); @Inject private UserEJB userEJB; private String name; private String email; private String password; private String confirmPassword; public void validatePassword(ComponentSystemEvent event) { FacesContext facesContext = FacesContext.getCurrentInstance(); UIComponent components = event.getComponent(); // get password UIInput uiInputPassword = (UIInput) components.findComponent("password"); String password = uiInputPassword.getLocalValue() == null ? "" : uiInputPassword.getLocalValue().toString(); String passwordId = uiInputPassword.getClientId(); // get confirm password UIInput uiInputConfirmPassword = (UIInput) components.findComponent("confirmpassword"); String confirmPassword = uiInputConfirmPassword.getLocalValue() == null ? "" : uiInputConfirmPassword.getLocalValue().toString(); // Let required="true" do its job. if (password.isEmpty() || confirmPassword.isEmpty()) { return; } if (!password.equals(confirmPassword)) { FacesMessage msg = new FacesMessage("Confirm password does not match password"); msg.setSeverity(FacesMessage.SEVERITY_ERROR); facesContext.addMessage(passwordId, msg); facesContext.renderResponse(); } if (userEJB.findUserById(email) != null) { FacesMessage msg = new FacesMessage("User with this e-mail already exists"); msg.setSeverity(FacesMessage.SEVERITY_ERROR); facesContext.addMessage(passwordId, msg); facesContext.renderResponse(); } } public String register() { User user = new User(email, password, name); userEJB.createUser(user); log.info("New user created with e-mail: " + email + " and name: " + name); return "regdone"; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getConfirmPassword() { return confirmPassword; } public void setConfirmPassword(String confirmPassword) { this.confirmPassword = confirmPassword; } } ``` ### 用戶輸入驗證 驗證規則: * 所有字段均為必填項(不允許使用任何空字段) * 合法的郵件地址 * 密碼必須至少包含 1 個小寫字符,1 個大寫字符,1 個數字 * 密碼長度必須在 6 到 20 個字符之間 * 確認密碼必須等于密碼 * 用戶電子郵件應該是唯一的(不用于其他注冊) ![User input form validation](https://img.kancloud.cn/24/54/245440883eedcb8a32601c614f7becc9_917x684.jpg) 用戶輸入表單驗證 有 3 種可能的方法來驗證 JSF 2 中的字段。我將演示全部 3 種方法。 **使用`JSFvalidateRegex`** 我們使用此方法驗證密碼字段。 我們要做的就是插入具有給定正則表達式模式的`f:validateRegex`標簽。 如果您想了解更多有關正則表達式的信息,請閱讀[基本 Java 正則表達式](https://javatutorial.net/basic-java-regular-expressions)教程 ```java <f:validateRegex pattern="((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,20})" /> ``` 我們還在`validatorMessage`屬性內的 JSF 文件中指定了錯誤消息 **在系統事件(`PostValidateEvent`)中驗證** 確認密碼字段通過名為`PostValidateEvent`的系統事件進行驗證。 我們在 XHTML 文件中注冊了一個新事件: ```java <f:event listener="#{registerView.validatePassword}" type="postValidate" /> ``` 用戶單擊“注冊”按鈕時,將觸發`RegisterView` bean 中的`public void validatePassword(ComponentSystemEvent event)`方法 **使用自定義驗證程序類** 對于電子郵件驗證,我們使用一個自定義類,該類實現`javax.faces.validator.Validator`接口 `Validator`類必須實現一個稱為`validate`的方法 ```java public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException ``` 在此方法中,我們根據正則表達式模式驗證用戶輸入,如果該模式不正確,我們將拋出`ValidatorException` ```java package net.javatutorial.tutorials.gfauthexample.validators; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.validator.FacesValidator; import javax.faces.validator.Validator; import javax.faces.validator.ValidatorException; @FacesValidator("emailValidator") public class EmailValidator implements Validator { private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\." + "[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*" + "(\\.[A-Za-z]{2,})$"; private Pattern pattern; private Matcher matcher; public EmailValidator() { pattern = Pattern.compile(EMAIL_PATTERN); } @Override public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { matcher = pattern.matcher(value.toString()); if (!matcher.matches()) { throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Invalid e-mail address", null)); } } } ``` 我們必須使用以下行在 XHTML 文件中包含給定字段的自定義驗證器 ```java <f:validator validatorId="emailValidator" /> ``` **檢查電子郵件是否已在另一個注冊中使用** 為此,我們不需要驗證器。 您可以在`ManagedBean` – `RegisterView`中使用類似這樣的簡單檢查 ```java if (userEJB.findUserById(email) != null) { // email is already used } ``` ## 成功注冊 如果用戶在注冊表單中輸入正確的數據,則將創建一個新的`User`對象并將其插入數據庫中。 ```java User user = new User(email, password, name); userEJB.createUser(user); ``` 請注意,我們還在`UserEJB#createUser`方法內為該用戶創建了`Group`對象 ```java Group group = new Group(); group.setEmail(user.getEmail()); group.setGroupname(Group.USERS_GROUP); em.persist(user); em.persist(group); ``` 數據庫條目如下所示: ![entry in users table](https://img.kancloud.cn/a9/e1/a9e1952036e4031b8fed61e92968c033_632x48.jpg) 用戶表中的條目 ![entry in user groups table](https://img.kancloud.cn/a1/cf/a1cf476022440ed3ecf7f189932d4c5c_252x48.jpg) 用戶組表中的條目 最后,用戶被重定向到`regdone.xhtml`頁面 ![regdone.xhtml page](https://img.kancloud.cn/69/82/6982771e436036b72e90481aab436008_365x131.jpg) `regdone.xhtml`頁面 ```java <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" > <h:head> <title>Registration done</title> </h:head> <h:body> <h3>Your account has been successfully created</h3> <br/><br/> <h:link value="Sign in with your new account" outcome="signin" /> </h:body> </html> ``` ## 登錄表單 在此表單中,我們需要用戶的電子郵件地址和密碼。 表單針對空白字段進行了驗證。 在`LoginView`中檢查用戶名和密碼的有效性。 ![Login form](https://img.kancloud.cn/97/92/979222aa978ad1016483494600fece55_363x243.jpg) 登錄表單 如果用戶輸入了有效的登錄數據,則會創建一個新的`java.security.Principal`對象。 我們還將用戶添加到`externalContext`的會話地圖中 ```java ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext(); Map<String, Object> sessionMap = externalContext.getSessionMap(); sessionMap.put("User", user); ``` `signin.xhtml`文件 ```java <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>login</title> </h:head> <h:body> <h:form> <h3>Please Sign In</h3> <h:panelGrid columns="2"> <h:outputLabel for="email">E-Mail:</h:outputLabel> <h:inputText id="email" value="#{loginView.email}" required="true" requiredMessage="Please enter your e-mail address"></h:inputText> <h:outputLabel for="password">Password:</h:outputLabel> <h:inputSecret id="password" value="#{loginView.password}" required="true" requiredMessage="Please enter password"></h:inputSecret> <h:commandButton action="#{loginView.login}" value="Login"></h:commandButton> </h:panelGrid> </h:form> <br /> <br /> <h:link value="Create new account" outcome="register" /> </h:body> </html> ``` `LoginView ManagedBean` ```java package net.javatutorial.tutorials.gfauthexample.managedbeans; import java.io.Serializable; import java.security.Principal; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.faces.application.FacesMessage; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import net.javatutorial.tutorials.gfauthexample.ejb.UserEJB; import net.javatutorial.tutorials.gfauthexample.entity.User; @ManagedBean @SessionScoped public class LoginView implements Serializable { private static final long serialVersionUID = 3254181235309041386L; private static Logger log = Logger.getLogger(LoginView.class.getName()); @Inject private UserEJB userEJB; private String email; private String password; private User user; public String login() { FacesContext context = FacesContext.getCurrentInstance(); HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest(); try { request.login(email, password); } catch (ServletException e) { context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Login failed!", null)); return "signin"; } Principal principal = request.getUserPrincipal(); this.user = userEJB.findUserById(principal.getName()); log.info("Authentication done for user: " + principal.getName()); ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext(); Map<String, Object> sessionMap = externalContext.getSessionMap(); sessionMap.put("User", user); if (request.isUserInRole("users")) { return "/user/privatepage?faces-redirect=true"; } else { return "signin"; } } public String logout() { FacesContext context = FacesContext.getCurrentInstance(); HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest(); try { this.user = null; request.logout(); // clear the session ((HttpSession) context.getExternalContext().getSession(false)).invalidate(); } catch (ServletException e) { log.log(Level.SEVERE, "Failed to logout user!", e); } return "/signin?faces-redirect=true"; } public User getAuthenticatedUser() { return user; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } ``` 最后,用戶被重定向到`/user/privatepage.xhtml` ![Private page](https://img.kancloud.cn/f5/c8/f5c8adb3f795abe487ba2558f4c0c74a_442x169.jpg) 私人頁面 ## 登出功能 注銷很簡單。 我們只需要執行注銷請求并使會話無效 ```java public String logout() { FacesContext context = FacesContext.getCurrentInstance(); HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest(); try { this.user = null; request.logout(); // clear the session ((HttpSession) context.getExternalContext().getSession(false)).invalidate(); } catch (ServletException e) { log.log(Level.SEVERE, "Failed to logout user!", e); } return "/signin?faces-redirect=true"; } ``` ## 故障排除 對安全代碼進行故障排除非常困難。 大多數組件不會引發有意義的異常。 這樣做是故意進行的,以防止黑客在嘗試破解應用程序的安全性時獲得有用的信息。 不幸的是,我們也沒有獲得這些有用的信息。 如果發現一些問題,請嘗試遵循例外。 兩次檢查您的安全領域配置,并檢查所有密碼編碼是否正確。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看