Version 1

    本文接着 开发基于JBoss AS 7.2.0的Java EE程序 - 05.如何配置安全域以及使用JAAS进行身份认证和权限控制 - part.1 描述。

     

    4. 用户注册

     

    4.1 war/UserRegisterMBean.java

    @ManagedBean

    @RequestScoped

    public class UserRegisterMBean {

        static final Logger logger = Logger.getLogger(UserRegisterMBean.class.getName());

        @EJB
        private IUserSession userSession;

        @EJB

        private ISensitiveNameSession sensitiveNameSession;

       

        private String username;//登录名

        private String password;//加了Transient注解:仅仅供用户从客户端传递到服务器端以便新增或者修改时使用。

        private String passwordRepeat;

        private String firstname = "";//真实姓

        private String lastname = "";//真实名

        private String mobilePhone = "";//手机

        private String email ="";//电子邮件。

        private String birthday = null;//生日

        private Gender gender = Gender.UNKNOWN;//性别

        //private String imageLink = "unknown.jpg";//头像链接

       

        public String getUsername() {

            return username;

        }

        public void setUsername(String username) {

            this.username = username;

        }

        ...//other getters/setters

       

        public String create(){

            //检测必须输入属性的合法性*********************************

            ...      

           

            //构造持久化对象*********************************

            User user = new User();

            //a:必须输入的数据

            user.setUsername(this.getUsername().trim());

            user.setHashedPassword(HashUtil.createPasswordHash(this.getUsername().trim(), this.getPassword().trim()));

            user.setEmail(this.getEmail().trim());

            //b:可选输入的数据

            if(this.getBirthday()!=null&&this.getBirthday().trim().length()> 0){

                user.setBirthday(DateHelper.stringToUtilDate(this.getBirthday().trim()));

            }

            user.setFirstname(this.getFirstname());

            user.setLastname(this.getLastname());

            user.setMobilePhone(this.getMobilePhone());

            user.setGender(this.getGender());

            //持久化对象*********************************

            try{

                userSession.registerUser(user);

            }catch(Exception e){

                String errMsg = "错误:"+e.getMessage();

                JSFHelper.addErrorMessage(errMsg);

                logger.severe(errMsg);

                return Constants.RETURN_FAILURE;

            }

           

            return Constants.RETURN_SUCCESS;

        }

    }

     

     

    4.2 war/register.xhtml

     

    <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <ui:composition xmlns="http://www.w3.org/1999/xhtml"

                    xmlns:ui="http://java.sun.com/jsf/facelets"

                    xmlns:f="http://java.sun.com/jsf/core"

                    xmlns:h="http://java.sun.com/jsf/html"

                    template="/template/template.xhtml">

     

        <ui:define name="title">

            <h:outputText value="#{messages['ybxiang.javaarm.register']}" />

        </ui:define>

     

        <ui:define name="headMetaData">

        </ui:define>

     

        <ui:define name="body">       

            <h:form id="form" >

     

                <h3><h:outputText value="#{messages['ybxiang.javaarm.register']}"/></h3>

     

                <h:panelGrid columns="2">

                    <h:outputLabel style="color:red" value="#{messages['ybxiang.javaarm.username']}(*)" for="username" />

                    <h:inputText id="username" value="#{userRegisterMBean.username}" maxlength="#{entitiesConstantsMBean.maxUsernameLength}" size="90" >

                        <f:validateLength minimum="#{entitiesConstantsMBean.minUsernameLength}" maximum="#{entitiesConstantsMBean.maxUsernameLength}" />

                    </h:inputText>

                    <h:outputText value=""/>

                    <h:message for="username" styleClass="message" errorClass="errormsg" infoClass="infomsg" warnClass="warnmsg"/>

                   

                    <h:outputLabel style="color:red" value="#{messages['ybxiang.javaarm.password']}(*)" for="password" />

                    <h:inputSecret id="password" value="#{userRegisterMBean.password}" maxlength="#{entitiesConstantsMBean.maxPasswordLength}" size="90" >

                        <f:validateLength minimum="#{entitiesConstantsMBean.minPasswordLength}" maximum="#{entitiesConstantsMBean.maxPasswordLength}" />

                    </h:inputSecret>

                    <h:outputText value=""/>

                    <h:message for="password" styleClass="message" errorClass="errormsg" infoClass="infomsg" warnClass="warnmsg"/>

     

                    <h:outputLabel style="color:red" value="#{messages['ybxiang.javaarm.passwordRepeat']}(*)" for="passwordRepeat" />

                    <h:inputSecret id="passwordRepeat" value="#{userRegisterMBean.passwordRepeat}" maxlength="#{entitiesConstantsMBean.maxPasswordLength}" size="90" >

                        <f:validateLength minimum="#{entitiesConstantsMBean.minPasswordLength}" maximum="#{entitiesConstantsMBean.maxPasswordLength}" />

                    </h:inputSecret>

                    <h:outputText value=""/>

                    <h:message for="passwordRepeat" styleClass="message" errorClass="errormsg" infoClass="infomsg" warnClass="warnmsg"/>

     

                    ...

                   

                    <h:outputText value="."/>

                    <h:commandButton id="submitButton" value="#{messages['ybxiang.javaarm.register']}" action="#{userRegisterMBean.create()}" />

                </h:panelGrid>

            </h:form>

        </ui:define>

    </ui:composition>

     

     

     

    4.3 HashUtil.java

    请参考:链接 一文。

    public class HashUtil {   

        public static final void testSystemPropertyInEJB(){

            System.out.println("SYSTEM_PROPERTY_KEY_JAAS_hashAlgorithm: "+System.getProperty(ServerConstants.SYSTEM_PROPERTY_KEY_JAAS_hashAlgorithm));

        }

       

        public static final String getHashAlgorithmFromSystemProperty(){       

            return System.getProperty(ServerConstants.SYSTEM_PROPERTY_KEY_JAAS_hashAlgorithm, ServerConstants.DEFAULT_JAAS_hashAlgorithm);

        }

        public static final String getHashEncodingFromSystemProperty(){

            return System.getProperty(ServerConstants.SYSTEM_PROPERTY_KEY_JAAS_hashEncoding, ServerConstants.DEFAULT_JAAS_hashEncoding);

        }

        public static final String getHashCharsetFromSystemProperty(){

            return System.getProperty(ServerConstants.SYSTEM_PROPERTY_KEY_JAAS_hashCharset, ServerConstants.DEFAULT_JAAS_hashCharset);

        }

       

        public static String createPasswordHash(String username, String password){

            return createPasswordHash(

                    getHashAlgorithmFromSystemProperty(),

                    getHashEncodingFromSystemProperty(),

                    getHashCharsetFromSystemProperty(),

                    username,

                    password

            );

        }

       

        public static String createPasswordHash(String hashAlgorithm, String hashEncoding,

                   String hashCharset, String username, String password){

            //method.1

            String result = org.jboss.security.auth.spi.Util.createPasswordHash(hashAlgorithm, hashEncoding, hashCharset, username, password);

           

            //method.2: from http://www.ajka-andrej.com/2011/05/22/jboss-6-client-authentication-sd/

            //String result2 = CryptoUtil.createPasswordHash(hashAlgorithm, hashEncoding, hashCharset, username, password);

           

            return result;

           

        }

    }

     

     

    4.4 ejb/IUserSession.java

    public interface IUserSession {
        public void registerUser(User user);
        ...
    }

     

     

    4.5 ejb/UserSession.java

    @Stateless
    @Local(IUserSession.class)
    public class UserSession implements IUserSession{
        @PersistenceContext()
        private EntityManager em;
       
        @EJB
        ICacheService cacheService;
        @EJB
        IEmailService emailService;   
        @EJB
        IJaasCacheSession jaasCacheSession;
       
        ...   
       
        @PermitAll()
        public void registerUser(User user){
            if(isUsernameExist(user.getUsername())){//已经有唯一性限制了
                throw new RuntimeException("该用户名已经备注册(This username already exists)。");
            }
           
            if(user.getEmail()==null){
                throw new RuntimeException("非法电子邮件地址(Invalid email address)。");
            }

            if(isEmailOverRegistered(user.getEmail())){
                throw new RuntimeException("一个电子邮件地址最多只能注册3个账号(One Email can register at most 3 accounts).");
            }
            //
            user.setActivated(false);//改成false,防止JSF MBean那里错误设置为true。测试的时候,我们用“true”,让用户直接处于激活状态。
            em.persist(user);

           
            ActivateCode activateCode = new ActivateCode();
            activateCode.setUser(user);
            activateCode.setCode(RandomHelper.getRandomString(ActivateCode.CODE_LENGTH));
            activateCode.setUsageType(ActivateCode.Usage.USER_ACTIVATE_ACCOUNT.id);
            em.persist(activateCode);
           
            //************************************UPDATE user cache!!!
            cacheService.updateUserCache_ONLY_by_UserSessionOrLocalEJB(user);

            //************************************Send activating code to user's email.
            Email email = new Email();
            email.setTitle(EmailConstants.USER_REGISTER_ACTIVATION_EMAIL_TITLE);
            email.setContent(EmailConstants.getUserRegisterActivatioinEmailContent(user.getUsername(), activateCode.getActivatingLink()));
            email.setToUser(user);
            em.persist(email);
            emailService.sendMail(email);
        }
        ...
    }

     

     

    5. JAAS身份认证 - 用户登陆

    5.1 web.xml

    我们需要首先在web.xml中配置security-constraint,如果GUEST访问了需要登陆才能访问的链接,那么他将被重定向到登陆页面:

     

    <?xml version="1.0" encoding="UTF-8"?>

    <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

        ...

        

        <!-- FORM <auth-method>

             refer to: http://www.avajava.com/tutorials/lessons/how-do-i-log-out-of-an-application-that-uses-form-authentication.html -->

        <!-- 1. Public Resources -->

        <security-constraint>

            <web-resource-collection>

                <web-resource-name>Public resources - Logged in User</web-resource-name>

                <description>Public resources - Logged in User</description>

                <url-pattern>/faces/createtopic.xhtml</url-pattern>

                <url-pattern>/faces/updatetopic.xhtml</url-pattern>

                <url-pattern>/faces/updatepost.xhtml</url-pattern>

                <url-pattern>/faces/message.xhtml</url-pattern>

                <url-pattern>/faces/search.xhtml</url-pattern>

                <url-pattern>/faces/message-action.xhtml</url-pattern>

                <url-pattern>/faces/send-message.xhtml</url-pattern>

                <url-pattern>/faces/upload.jsp</url-pattern>

                <url-pattern>/logoutServlet</url-pattern>

                <url-pattern>/upload.xhtml</url-pattern>

                <url-pattern>/uploadServlet</url-pattern>

                <url-pattern>/UploadServlet</url-pattern>

            </web-resource-collection>

            <auth-constraint>

                <role-name>*</role-name>

            </auth-constraint>

            <user-data-constraint>

                <transport-guarantee>NONE</transport-guarantee>

            </user-data-constraint>

        </security-constraint>

        <security-constraint>

            <web-resource-collection>

                <web-resource-name>Public resources - guest</web-resource-name>

                <description>Public resources - guest</description>

                <url-pattern>/faces/activate.xhtml</url-pattern>

                <url-pattern>/faces/activate-success.xhtml</url-pattern>

                <url-pattern>/faces/display.xhtml</url-pattern>

                <url-pattern>/faces/index.xhtml</url-pattern>

                <url-pattern>/faces/index-forum.xhtml</url-pattern>

                <url-pattern>/faces/list.xhtml</url-pattern>

                <url-pattern>/faces/register-success.xhtml</url-pattern>

            </web-resource-collection>

            <user-data-constraint>

                <transport-guarantee>NONE</transport-guarantee>

            </user-data-constraint>

        </security-constraint>

     

     

     

        <!-- 2. CONFIDENTIAL resources -->
        <security-constraint>
            <web-resource-collection>
                <web-resource-name>CONFIDENTIAL resources - logged in user</web-resource-name>
                <description>CONFIDENTIAL resources - logged in user</description>
                <url-pattern>/faces/login-https.xhtml</url-pattern>
                <url-pattern>/faces/console/*</url-pattern>
            </web-resource-collection>
            <auth-constraint>
                <role-name>*</role-name>
            </auth-constraint>
            <user-data-constraint>
                <transport-guarantee>CONFIDENTIAL</transport-guarantee>
            </user-data-constraint>
        </security-constraint>
        <security-constraint>
            <web-resource-collection>
                <web-resource-name>CONFIDENTIAL resources - logged in Administrator</web-resource-name>
                <url-pattern>/faces/system/*</url-pattern>
            </web-resource-collection>
            <auth-constraint>
                <role-name>Administrator</role-name>
            </auth-constraint>
            <user-data-constraint>
                <transport-guarantee>CONFIDENTIAL</transport-guarantee>
            </user-data-constraint>
        </security-constraint>
        <security-constraint>
            <web-resource-collection>
                <web-resource-name>CONFIDENTIAL resources - guest</web-resource-name>
                <description>CONFIDENTIAL resources - guest</description>
                <url-pattern>/faces/register.xhtml</url-pattern>
                <url-pattern>/faces/login-form.xhtml</url-pattern>
            </web-resource-collection>
            <user-data-constraint>
                <transport-guarantee>CONFIDENTIAL</transport-guarantee>
            </user-data-constraint>
        </security-constraint>


        <security-role>
            <role-name>*</role-name>
        </security-role>
        <login-config>
            <auth-method>FORM</auth-method>
            <form-login-config>
                <form-login-page>/faces/login-form.xhtml</form-login-page>
                <form-error-page>/faces/login-fail.xhtml</form-error-page>
            </form-login-config>
        </login-config>
       
    </web-app>

     

     

    5.2 login-form.xhtml

    这是我们的登陆页面。

     

    <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <ui:composition xmlns="http://www.w3.org/1999/xhtml"
                    xmlns:ui="http://java.sun.com/jsf/facelets"
                    xmlns:f="http://java.sun.com/jsf/core"
                    xmlns:h="http://java.sun.com/jsf/html"
                    template="/template/template.xhtml">

        <ui:define name="title">
            <h:outputText value="#{messages['ybxiang.javaarm.login']}" />
        </ui:define>

        <ui:define name="headMetaData">
        </ui:define>

        <ui:define name="body">

     

            <h:panelGroup layout="block" rendered="#{request.getUserPrincipal() == null}" >
                <!--
                (1) <form method="post" action="j_security_check">
                    http://javaarm.com/faces/j_security_check is used!
                (2) <form method="post" action="#{request.contextPath}/j_security_check">
                    http://javaarm.com/j_security_check is used!
                -->
                <form method="post" action="j_security_check">
                    <table>
                        <tr>
                            <td width="10%">#{messages['ybxiang.javaarm.username']}:</td>
                            <td width="40%"><input type="text" name="j_username" /></td>
                        </tr>
                        <tr>
                            <td>#{messages['ybxiang.javaarm.password']}:</td>
                            <td><input type="password" name="j_password" /></td>
                        </tr>
                        <tr>
                            <td colspan="2"><input type="submit" value="#{messages['ybxiang.javaarm.login']}" /></td>
                        </tr>
                    </table>
                </form>
            </h:panelGroup>
           
        </ui:define>
    </ui:composition>