zoukankan      html  css  js  c++  java
  • BOS3

    1、用户登录认证自定义拦截器编码  

      用户登录的校验,通过拦截器来进行过滤

      这里肯定是通过Struts2的拦截器进行功能的实现了。intecepter

      MethodFilterIntercepter拦截器增强:可以对action里面的业务方法进行拦截设置。

      MethodFilterIntercepter有两个属性

    protected Set<String> excludeMethods = Collections.emptySet();
    protected Set<String> includeMethods = Collections.emptySet();

      excludeMethods - 要从拦截器处理中排除的方法名称

      includeMethods - 要包含在拦截器处理中的方法名称

      如果方法名称在includeMethods和excludeMethods中都可用,则它将被视为包含的方法:includeMethods优先于excludeMethods

      那么现在要做的就是进行loginInteceptor拦截器的编写,以及对login方法的放行。

    (1)LoginInteceptor类(拦截器,判断是否登录。未登录的返回到登录界面。无法跳过登录直接进入系统界面)

       1、继承MethodFilterInterceptor(注意,这是一个类而不是接口)

        

    @SuppressWarnings("all")
    @Component("loginInterceptor")
    public class LoginInteceptor extends MethodFilterInterceptor{
        @Override
        protected String doIntercept(ActionInvocation invocation) throws Exception {
            //判断用户是否登录,从session中找用户信息
            User existUser=(User)ServletActionContext.getRequest().getSession().getAttribute("exitUser");
            //未登录,返回未登录错误码
            if(existUser==null) {
                return "no_login";
            }else {
                //如果已经登录了,调用下一个组件。通过invocation去调用invoke方法。
                return invocation.invoke();
            }
        }
    }

      如果session中有用户信息就证明以及登录过了。调用下一个组件,通过invocation的invoke方法调用。

      拦截器由Struts2的前端控制器来创建,交由spring进行管理。这里拦截器设置成单例就可以了。

      2、在struts.xml中进行注册

        不能讲拦截器写在action标签中,这样只有走这个action的时候才会执行拦截器。

        要写在package标签中

        

        <package name="bos" extends="json-default">
            <!-- 注册拦截器 -->
            <interceptors>
                <!-- 通过伪类名来注册拦截器,因为是由spring创建的,所以使用@Component("loginInterceptor")的名字就可以了,一定要一致 
                    。完成注册。放入栈中。这个拦截器对所有action都有效 -->
                <interceptor name="myLogin" class="loginInterceptor">
                </interceptor>
                <!-- 栈 加一个是默认栈,必须要加。 -->
                <interceptor-stack name="mystack">
                    <!-- 自己定义拦截器方法拦截设置 -->
                    <interceptor-ref name="myLogin">
                        <!-- 这里一定要写excludeMethods,执行login方法的时候不会执行拦截器操作。 -->
                        <param name="excludeMethods">login</param>
                    </interceptor-ref>
                    <!-- 名称必须是defaultStack :默认栈 -->
                    <interceptor-ref name="defaultStack"></interceptor-ref>
                </interceptor-stack>
            </interceptors>
            <!-- 拦截器作用域。注册完之后启动,拦截器的作用域。需要对所有拦截器都有效 .它对这个包里面的所有的action都有效。但是userAction也在因为设置了它的父包是bos 
                @ParentPackage("bos"),因此对它的UserAction下的方法也有效。 但是由于login方法需要走拦截器,如果login方法都需要登录的话,是永远登录不上的。 
                如果登录就会提示找不到方法,登录不上。 -->
        <!-- 不写这段代码的话就相当于拦截器没有执行,只是定义了 -->
            <default-interceptor-ref name="mystack"></default-interceptor-ref>
    
            <!-- 定义全局结果集视图 -->
            <global-results>
                <result name="no_login" type="redirect">/login.jsp</result>
            </global-results>
            <action name="index">
                <result>/index.jsp</result>
            </action>
            <!-- 需要进行权限控制的页面访问 -->
            <action name="page_*_*">
                <result type="dispatcher">/WEB-INF/pages/{1}/{2}.jsp</result>
            </action>
        </package>

      

      package
    Content Model : (result-types?, interceptors?, default-interceptor-ref?, default-action-ref?, default-
    class-ref?, global-results?, global-exception-mappings?, action*)

      按照这个来编写顺序。

      首先进行来loginInceptor注册。这里要通过伪类名进行注册,因为是有spring进行创建的。@Component("loginInterceptor")

      

    <interceptor name="myLogin" class="loginInterceptor"></interceptor>

      所以这里的class填写@Component的名称就可以了。

      拦截器栈:

      

                <!-- 栈 加一个是默认栈,必须要加。 -->
                <interceptor-stack name="mystack">
                    <!-- 自己定义拦截器方法拦截设置 -->
                    <interceptor-ref name="myLogin">
                        <!-- 这里一定要写excludeMethods,执行login方法的时候不会执行拦截器操作。 -->
                        <param name="excludeMethods">login</param>
                    </interceptor-ref>
                    <!-- 名称必须是defaultStack :默认栈 -->
                    <interceptor-ref name="defaultStack"></interceptor-ref>
                </interceptor-stack>

      在栈内添加自定义的拦截器,并设置这个拦截器对login方法放行。这里的name一定要完全写excludeMethods,MethodFilterInteceptor的属性名称。

      然后本地栈一定要进行添加。

         <!-- 名称必须是defaultStack :默认栈 -->
     <interceptor-ref name="defaultStack"></interceptor-ref>

      这样是完成了拦截器的注册,但是需要进行拦截器作用域的设置,否则该拦截器是不生效的。它对这个bos包中的所有action都有效。由于UserAction因为设置了父包为bos。

    @ParentPackage("bos")

      因此它对UserAction下的方法也有效。所以UserAction的login方法需要走拦截器,但是如果login方法也要走loginInteceptor拦截器的话,那它永远是登录不上的。所以要添加

    在这个拦截器中添加excludeMethods

      拦截器作用域,这句话是所有action自动调用的拦截器栈。(个人理解为将自己设置的栈添加到默认拦截器栈中,所有页面都要走这个拦截器)

    <default-interceptor-ref name="mystack"></default-interceptor-ref>

      在未检测到session中有用户信息的时候。返回no_login.定义它的全局结果集。

      

            <!-- 定义全局结果集视图 -->
            <global-results>
                <result name="no_login" type="redirect">/login.jsp</result>
            </global-results>

      及:未通过登录直接跳转到/login.jsp页面。

      

    2、在页面显示用户的用户名和IP

      

    [<strong>超级管理员</strong>],欢迎你!您使用[<strong>192.168.1.100</strong>]IP登录!
    [<strong>超级管理员</strong>],欢迎你${sessionScope.existUser.username}!
    您使用[<strong>${pageContext.request.remoteAddr}</strong>]IP登录!

      显示从session中获取的数据。

    3、修改密码的正则校验

        

      首先一个弹窗:EasyUI提供了这种功能:
      窗体里显示用户的一些信息
      在点击修改密码的时候弹出一个窗口。

      

            function editPassword() {
            $('#editPwdWindow').window('open');
        }
        添加点击事件
                <div class="easyui-layout" fit="true">
                <div region="center" border="false" style="padding: 10px; background: #fff; border: 1px solid #ccc;">
                    <table cellpadding=3>
                        <tr>
                            <td>新密码:</td>
                            <td><input id="txtNewPass" type="Password" class="txt01" /></td>
                        </tr>
                        <tr>
                            <td>确认密码:</td>
                            <td><input id="txtRePass" type="Password" class="txt01" /></td>
                        </tr>
                    </table>
                </div>
                <div region="south" border="false" style="text-align: right; height: 30px; line-height: 30px;">
                    <a id="btnEp" class="easyui-linkbutton" icon="icon-ok" href="javascript:void(0)" >确定</a> 
                    <a id="btnCancel" class="easyui-linkbutton" icon="icon-cancel" href="javascript:void(0)">取消</a>
                </div>
    
                其中确定的ID   id="btnEp"

    修改密码需要定义一些东西:比如,不能不输入,两次输入要一致,密码长度限制
    这些限制和校验都是在确定里面做的。
    确定添加点击事件。

      

        $("#btnEp").click(function(){
                //修改密码的实现方法
                //1、获取新密码,判断这个密码是否有效    JS的校验     (是否为空。特殊字符,长度)
                var newPwd=$("#txtNewPass").val();
                if(newPwd==""||newPwd==null){
                    $.messager.alert("警告","新密码必须填写","warning");
                    return;
                }
                //空白字符
                var reg=/s+/;  //+代表一个。这里是有一个或多个空白字符就会被检测到
                if(reg.test(newPwd)){
                    $.messager.alert("警告","密码不能包含空格","warning");
                    return;
                }
                //定义长度,不在这个长度范围的就会提示
                if(newPwd.length<=0||newPwd.length>=7){
                    $.messager.alert("警告","密码为1-6位","warning");
                    return;
                }
                //2、重复密码判断    两次密码要求一致  新旧密码不一致就会提示
                if(newPwd!=$("#txtRePass").val()){
                    $.messager.alert("警告","密码不一致","warning");
                    return;
                }
                //3、密码一致   有效   发送ajax请求给action --->service--->Dao  数据库    当前用户密码的update操作
                //4、关闭窗口
            });

    4、当前action的父包是bos,而bos继承“json-default” ,所以可以用json格式

     editPassword方法

        @Action(value = "userAction_editPassword",results= {@Result(name="editPassword",type="json")})
        public String editPassword() {
            try {
            User existUser=(User)getSessionAttribute("existUser");
            serviceFacade.getUserService().editPassword(model.getPassword(),existUser.getId());
            push(true);//压栈
            }catch (Exception e) {
                e.printStackTrace();
                push(false); //给到页面
            }
            return "editPassword";
        }
        //修改密码  spring  data  jpa
        @Modifying
        @Query("update User set password=?1 where id=?2") //只有这个不能修改,因为这个是查找,必须加@Modifying才可以修改
        public void editPassword(String password, String id);

    这里必须添加 @Modifying注解,因为@Query是只限于查询。

      涉及到增删改的都要添加事务。在配置文件中添加jps事务管理。

      

           <!-- spring  data jpa 事务管理 配置 -->
          <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
            <property name="entityManagerFactory" ref="entityManagerFactory" />
        </bean>
    //打开注解扫码
    <tx:annotation-driven transaction-manager="transactionManager"/>

     5、json-plugin插件原理说明

      

        @Action(value = "userAction_editPassword",results= {@Result(name="editPassword",type="json")})
        public String editPassword() {
            try {
            User existUser=(User)getSessionAttribute("existUser");
            serviceFacade.getUserService().editPassword(model.getPassword(),existUser.getId());
            push(true);
            }catch (Exception e) {
                e.printStackTrace();
                push(false); //给到页面
            }
            return "editPassword";
        }

      上面的代码是修改了密码后将结果集压栈。所以在栈顶的就是true/fasle。

      结果集使用的是json-default,

      action的父包是bos,而bos又是json-default的子包。

    <package name="bos" extends="json-default">

    所以type=json的底层就是走下面的这个结果集

      

            <result-types>
                <result-type name="json" class="org.apache.struts2.json.JSONResult"/>
            </result-types>

      底层是通过response返回ajax请求,数据来自createJSONString(HttpServletRequest,Object);

      

    会判断是否有用户自定义配置,如果有用户自定义配置就找到名称为root的值,obj。如果没有就去值栈中获取栈顶的元素。

    6、 取派员表和实体类生成

      利用power designer生成数据表,然后通过反向生成实体类来建立表。

      先配置reveng.xml文件。

    reveng.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-reverse-engineering PUBLIC "-//Hibernate/Hibernate Reverse Engineering DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-reverse-engineering-3.0.dtd" >
    
    <hibernate-reverse-engineering>
        <!-- 以下标签是Oracle使用的 -->
        <!-- <schema-selection match-table="T_USER" match-schema="SH1208"/> -->
        <!-- <schema-selection match-table="BC_.*" match-schema="SH1208"/> -->
        <!-- <table name="BC_DECIDEDZONE" schema="SH1208" -->
        <!-- class="com.zero.bos.domain.bc.DecidedZone"> -->
        <!-- <primary-key> -->
        <!-- <generator class="uuid"></generator> -->
        <!-- </primary-key> -->
        <!-- </table> -->
        <!-- match-table:访问表的 (代表这个表能够被找到).对于数据库zero_bos下面t_user表定义实体类生成规则 -->
        <!-- 可以被扫码到 -->
        <schema-selection match-table="t_user" match-catalog="zero_bos" />
        <!--.代表任意字符,*代表0或多个 -->
        <schema-selection match-table="bc_.*" match-catalog="zero_bos" />
        <!-- 实体类生成规则定义, -->
        <table name="bc_staff" catalog="zero_bos" class="com.zero.bos.domain.bc.Staff">
            <primary-key>
                <!-- uuid主键(字符) identity:主键自增长 -->
                <generator class="uuid"></generator>
            </primary-key>
        </table>
    
        <table name="bc_decidedzone" catalog="zero_bos" class="com.zero.bos.domain.bc.DecidedZone">
            <primary-key>
                <!-- uuid主键(字符) identity:主键自增长 -->
                <generator class="uuid"></generator>
            </primary-key>
        </table>
    
        <table name="bc_region" catalog="zero_bos" class="com.zero.bos.domain.bc.Region">
            <primary-key>
                <!-- uuid主键(字符) identity:主键自增长 -->
                <generator class="uuid"></generator>
            </primary-key>
        </table>
    
        <table name="bc_subarea" catalog="zero_bos" class="com.zero.bos.domain.bc.Subarea">
            <primary-key>
                <!-- uuid主键(字符) identity:主键自增长 -->
                <generator class="uuid"></generator>
            </primary-key>
        </table>
        <!-- 实体类生成规则定义, -->
        <table name="t_user" catalog="zero_bos" class="com.zero.bos.domain.user.User">
            <primary-key>
                <!-- uuid主键(字符) identity:主键自增长 -->
                <generator class="uuid"></generator>
            </primary-key>
        </table>
    </hibernate-reverse-engineering>

      然后maven执行:hibernate3:hbm2java

      建模完成。

    7、取派员的增加

      点击保存按钮,实现取派员信息的添加。

      

    1         <div region="north" style="height:31px;overflow:hidden;" split="false" border="false" >
    2             <div class="datagrid-toolbar">
    3                 <a id="save" icon="icon-save" href="#" class="easyui-linkbutton" plain="true" >保存</a>
    4             </div>
    5         </div>
    6         
    7         <div region="center" style="overflow:auto;padding:5px;" border="false">
    8             <form id="addStaffForm" action="${pageContext.request.contextPath }/bc/staffAction_save" method="post">
    9                 <table class="table-edit" width="80%" align="center">

    对click添加点击事件

     

    编写action代码

      

        // 取派员添加
        @Action(value = "staffAction_save", results = {
                @Result(name = "save", location = "/WEB-INF/pages/base/staff.jsp") })
        public String save() {
            try {
                serviceFacade.getStaffService().save(model);
            } catch (Exception e) {
                e.printStackTrace();
                push(false);
            }
            return "save";
        }
    转发是可以到web-inf目录下的,重定向不行。

    父类中注入门面类

      

    // 父类中 注入 门面业务层
    @Autowired
    protected FacadeService serviceFacade;

      DAO继承

    public interface StaffDao extends JpaRepository<Staff, String> {
        
    }

    完成

      

    JavaEE 开发 
    1: 客户端 页面 js 编写 (easyui +jquery)
    
    2:服务器端:  接受请求数据?
    Action( 类名必须以Action 结尾  包名一定要包含  action|actions|struts|struts2  @Controller  @Scope @Namespace @ParentPackage)  
    -service  (注解开发  接口+实现类(@Service  @Transaction)
    dao  接口 继承 JPARepostitory
    )
    
    3:结果集跳转 (转发跳转 WEB-INF   查询: ajax)

    8、取派员的分页查询和显示

      (1)首先页面使用的假数据格式是这样的

    {                                                      
        "total":100,    
        "rows":[ 
            {"id":"001","name":"李大","telephone":"13912345678","haspda":"1","deltag":"0","standard":"10-20公斤","station":"杭州分部"},
            {"id":"002","name":"李二","telephone":"13912345678","haspda":"1","deltag":"0","standard":"10-20公斤","station":"杭州分部"}
        ]
    }

      所以我们后台返回的数据也要是这种格式的才可以正常显示。

      (2)easyui对分页查询的实现

        从服务器端拿json格式数据。上面的数据就是easyui框架需要的分页数据

        要求服务器端是数据格式要有:(1)total:取派员总记录数
                                              (2)rows:每页记录数  [{},{}] 要求是数组    

        然后ajax json数据回送    easyui插件自动实现分页

        客户端:进行分页查询的时候,客户端必须提供   请求页码和记录数

        在点击下一页的时候就会向url发送请求,并且带参数

        page和rows,这个插件在进行分页查询 时候,它会自动的向我们的datagrid的url地址发送最新的页码和每页记录数

        然后后台代码会进行数据库查询再返回数据

        

      分页查询的结论:客户端  url:实际的服务器请求地址

      

    url : "${pageContext.request.contextPath}/bc/staffAction_pageQuery",

    服务器端:BaseAction接收page和rows即可。

    staffAction---->service--->dao-->List<Staff>

            但是List<Staff>序列号会变成
            [{},{},{}]   缺少total

            所以服务器端要对其进行扩展
            这种数据格式最典型的就是map
            所以要把数据往map里放
            map.putTotal
            map.putData
            栈顶--->json-plugin
            自动map--->json    字符串

    功能实现:

        (1)在BaseAction中添加  

        // 分页操作 接受页面 和 每页显示记录
        protected int page;// 页码
        protected int rows;// 每页显示记录数
        //struts2属性注入 将请求数据  自动注入
    
        public void setPage(int page) {
            this.page = page;
        }
    
        public void setRows(int rows) {
            this.rows = rows;
        }

    (2)将page和rows传递给StaffAction  

      

        // 取派员分页查询
        @Action(value = "staffAction_pageQuery", results = {
                @Result(name = "pageQuery", type="json") })
        public String pageQuery() {
            Map<String,Object> data=new HashMap<String,Object>();
            try {
                PageRequest pageable=new PageRequest(page-1, rows);
                Page <Staff> pageData=serviceFacade.getStaffService().pageQuery(pageable);
                //dao  参数   Pageable pageable   自动完成分页查询   将分页结果数据   自动封装到Page<T>,所以这里不能是List<Staff>,否则无法封装
                
                //从Page  对象获取总记录数  和   每页分页记录数List<Staff>
                data.put("total", pageData.getTotalElements());
                data.put("rows", pageData.getContent());
                push(data);
            } catch (Exception e) {
                e.printStackTrace();
                push(false);
            }
            return "pageQuery";
        }

      底层两个参数page和rows走的是dao层。

      StaffDao是继承JpaRepository<Staff, String>

      而JpaRepository接口继承PagingAndSortingRepository
            @NoRepositoryBean
            public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {

    ----------
            @NoRepositoryBean
            public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
    
    ----------
            @NoRepositoryBean
            public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
    
            这里就可以看到PagingAndSortingRepository做的分页。所以要实现分页功能,要按它的要求提供相关的参数。
            `@NoRepositoryBean
            public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
    
        /**
         * Returns all entities sorted by the given options.
         * 
         * @param sort
         * @return all entities sorted by the given options
         */
        Iterable<T> findAll(Sort sort);
    
        /**
         * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.
         * 
         * @param pageable
         * @return a page of entities
         */
        Page<T> findAll(Pageable pageable);
    }`
        
    ----------

        需要 Pageable,给它这个,它就会自动完成分页。结果数据
        `Page <Staff> pageData=serviceFacade.getStaffService().pageQuery(page,rows);`
        这里的Pageable也是一个接口。所以要看它有哪些实现类和方法。
        它只有一个PageRequest实现类。

      为spring  jpa服务,要讲页码和每页记录数封装到Pageable

          easyui要的map格式,所以要把page对象放到map中,这样就可以序列化成想要的样式。
        在么有配root的情况下,easyui从栈顶拿序列化的数据。
        为什么不能直接序列化pageData而是要先放入map再进行序列化。
        
        如果此时添加取派员,数据库会增加,但是前端页面并不会显示添加的信息。
        同时后台会报错,延迟加载错误。

          Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.zero.bos.domain.bc.Staff.decidedZones, no session or session was closed

      典型的延迟加载的错误。

    问题原因:

          前面每页10条的时候不需要分页,每页去查下一页。现在更改为每页1条时,就需要查下一页。会用到limit
          每页立刻去发送语句查询。
          原理:事务管理    ---service层
             事务结束   session就没有了
             延迟数据(entitymanager)在的一级缓存区里。
      没有分页查询的语句,只有记录总数的。

          对staff的this.decidedZones = decidedZones;是延迟的,不用的时候不加载。

      


        StaffAction   Page<Staff> ----- >延迟的定区数据set
        action  没有延迟数据只有Staff数据
        会去找是
        不掉get方法就不会序列号

        @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "staff")
        @JSON(serialize=false)
        public Set<DecidedZone> getDecidedZones() {
            return this.decidedZones;
        }


     问题2:
        但是这个只有只有一页,而且显示的数据并不是第一条的数据。
        查询是从第二页查的,而不是第一页。
        问题出在action中。
        在PageRequest源码看就会知道,它的页面是从0开始的。第一页的下标是0,所以这里我们让它-1就可以了。
        但是总页面还是不对。那么就是数据格式显示的问题,total数据没有传递过来。
        问题3:当我们点击取派员的时候,格式显示的不正确,

      原因是jps默认的起始页是0.所以page要-1.
        

          Page<Staff>  没有有延迟的那个数据,只有satff的数据,没有延迟的数据。
        数据放入值栈,    json-plugin序列号数据
        调用Staff实体类所有的getter  获取数据才能json字符生成
        但是集合 延迟数据已经没有了,没有session了。就会报错。
        @JSON 插件从值栈中获取数据  进行json字符串生成的时候  调用目标对象get方法! 
        Serialize=false 插件不会调用 get 方法获取数据

      Page<T> findAll=   ;一行就实现了分页。

     9、小结:

        hibernate默认是lazy,懒加载。没有向数据库发数据,但是缓存有。

        有这些数据都是基于事务的,一旦事务结束。数据就没有了。

        或者可以把事务放到web层,放大。web层一直存在。

        

      

      

    坚持就是胜利
  • 相关阅读:
    2 初学函数,求幂指数练手程序
    1 批量生成虚拟姓名
    Python 中 time 模块与 datetime 模块在使用中的不同之处
    feature selection&feature abstraction降维
    拿到样本简单的清洗操作
    使用sklearn做单机特征工程
    tensorflow安装
    PCA数学角度解析
    使用Python进行描述性统计【解决了实习初期的燃眉之急】
    类、对象、属性、方法、类的成员
  • 原文地址:https://www.cnblogs.com/xiaotieblog/p/8722718.html
Copyright © 2011-2022 走看看