• JeeSite功能模块解读,功能介绍,功能实现


    做为十分优秀的开源框架,JeeSite拥有着很多实用性的东西。

    首先说下他的一个流程

    Jeesite流程

    流程

    主要是jsp,entity,dao,dao.xml,service,controller)

    (1) .MyBatisRegisterDao.xml

    这里做的工作便是对数据库语句的撰写。

     

    (2) .MyBatisRegisterDao.java

     

    (3) .Register.java实体

    一般公共的字段放在相应的实体工具类中,如createBycreateDateupdateByupdateDateremarksdel_flag都放在dateEntity.java中。用时只需extends即可

     

    (4).RegisterService.java

     

    (4) .RegisterController.java

    其中建议requestMapping注解放在首位,全局注解为好。

     

     

    (6).Register.jsp

     

    Mybatis的总体流程是

    1.加载配置并初始化,其触发条件是加载配置文件将SQL 的配置信息加载成为一个个MappingStatement对象(包括传入参数映射配置,执行的sql语句,结果映射配置) 存储在内存中

    2.接收调用请求,其触发条件是调用mybatis中的api,将请求传递给下层的请求处理层处理

    3.处理操作请求api接口层传递传递请求过来,传入sqlid和传入参数,根据sqlid查找对应的MappingStatement对象,和传入参数对象解析MappingStatement对象,得到最终要执行的sql和执行传入参数,后获取数据库连接,根据最终得到的sql语句和传入参数到数据库执行,得到最终的处理结果,最后释放资源

    4.将最终处理结果返回

    关于shiro授权

    1.Shiro授权的三要素是:权限,角色,用户

    2.三要素的关联:因为通过声明权限我们仅仅能了解这个权限在项目中能做什么,而不能确定谁有这个权限,所以,我们需要在应用程序中对用户和权限建立关系。

    3.在项目上: 我们一般将权限分配给某个角色,然后将这个角色分配给一个或多个用户,例如:修改的权限是只有管理员才拥护的,那么,在这个时候,管理员就相当于被设于拥有修改权限的用户,

    4.shiro支持三种授权方式:编码实现,注解实现,jsp Tag实现

     

    我们看下关于用户权限的几个表:

     

    Orm对象关系映射

    1.用于实现面向对象编程语言里不同类型系统的数据之间的转换

    2.jeesite框架中用到的就是mybatis

    安全模块

    用户密码加密存储

    用户密码加密算法:对用户的密码进行sha-1算法加密。迭代1024次,并将salt放在前16位中。

    /**

     * 生成安全的密码,生成随机的16位salt并经过1024次 sha-1 hash

     */

    public static String entryptPassword(String plainPassword) {

    String plain = Encodes.unescapeHtml(plainPassword);

    byte[] salt = Digests.generateSalt(SALT_SIZE);

    byte[] hashPassword = Digests.sha1(plain.getBytes(), salt, HASH_INTERATIONS);

    return Encodes.encodeHex(salt)+Encodes.encodeHex(hashPassword);

    }

    我们可以看到,在SystemService中,加密方式经过1024次迭代,并将salt放在前16位。Return的首先是salt然后+hashPasswordd.

    然后看下解密:

    /**

     * 设定密码校验的Hash算法与迭代次数

     */

    @PostConstruct

    public void initCredentialsMatcher() {

    HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(SystemService.HASH_ALGORITHM);

    matcher.setHashIterations(SystemService.HASH_INTERATIONS);

    setCredentialsMatcher(matcher);

    }

    jeesite/src/main/java/com/thinkgem/jeesite/modules/sys/security/SystemAuthorizingRealm.java

    解密的过程与加密的过程是一致的。

    安全验证码

    验证码一般不会出现。但是当用户请求超过三次,此时sysLogin.jsp会向ValidateCodeServlet请求验证图片,而ValidateCodeServlet生成的图片则存入session中。然后进行code的一个验证。

    String validateCode = request.getParameter(VALIDATE_CODE);

    if (StringUtils.isNotBlank(validateCode)){

    response.getOutputStream().print(validate(request, validateCode)?"true":"false");

    缓存处理

    系统对每个用户所需要用到的资源都用map做了缓存处理。

    如果用户不存在则创建一个新的Map<String,Object>对象,如果存在的话则取principal中的Map<String,Object>对象做为缓存,因为principle会随着用户的logout自动释放,每个用户都有了自己的缓存,可以再日志中查询到。并且每个用户的缓存是相互独立的。

    UserUtils.java中,

    public static Map<String, Object> getCacheMap()

    public static Object getCache(String key, Object defaultValue)

    public static void putCache(String key, Object value)

    public static void removeCache(String key)

    主题更换

    head.jsp中通过查询cookie.theme.value的值来替换bootstrap的css文件,从而达到主题更换的效果。我们先看下head.jsp:

    <link href="${ctxStatic}/bootstrap/2.3.1/css_${not empty cookie.theme.value ? cookie.theme.value : 'cerulean'}/bootstrap.min.css" type="text/css" rel="stylesheet" />

    LoginController中,主题替换的接口如下:

    /**

     * 获取主题方案

     */

    @RequestMapping(value = "/theme/{theme}")

    public String getThemeInCookie(@PathVariable String theme, HttpServletRequest request, HttpServletResponse response){

    if (StringUtils.isNotBlank(theme)){

    CookieUtils.setCookie(response, "theme", theme);

    }else{

    theme = CookieUtils.getCookie(request, "theme");

    }

    return "redirect:"+request.getParameter("url");

    }

    翻页处理

    /jeesite/src/main/java/com/thinkgem/jeesite/common/persistence/Page.java

    其中page<T>的toString()方法实现了BootStrap的显示细节,其中数据都放于Page中。

    而在前端jsp页面只需要引用即可。

    <div class="pagination">${page}</div>

    树形目录选择

    先说下office的弹出对话框式树形选择。

    使用tags:treeselect标签将页面操作逻辑封装。在tags:treeselect中,用JBox来调用/tag/treeselect转向treeselect.jsp页面,并传入相关的参数,其中url,展示的json格式数据来源。当选择的为v时,即确定,这时,id和name就hi传出来。

    ajaxData:{selectIds: $("#${id}Id").val()},buttons:{"确定":"ok", ${allowClear?""清除":"clear", ":""}"关闭":true}, submit:function(v, h, f){

    $("#${id}Id").val(ids.join(",").replace(/u_/ig,""));

    $("#${id}Name").val(names.join(","));

    其中tagTreeselect.jsp负责数据展示。

    zNodetree负责选择等操作。

     

    角色授权树型选择操作

    先通过后台传过来的数据构建zNodetree

    zNodetree 来管理数据的选择

    在表单提交时(submitHandler )获取选择数据并添加到相应的 input中。然后提交。如下图

     

    这里用了一个小技巧。 SpringMVC  进行前后台数据绑定的时候其实是调用Model 的 set 与 get方法。( 所以只要有这两个方法即可,不用有成员变员也行)

    Role 模型添加了两个方法,并用 Transient 来标记不写入数据库。如下

     

    这样就可以自动把数据写回到Role 中

     

     

     

    默认根路径跳转

    定义了无Controller的path<->view直接映射

    <mvc:view-controller  path=”/”  view-name=”redirect:${web.ex}” />

    登陆login模块

    定义了1.sysLogin.jsp

    整个jsp可以看做一个表单。主要目的就是接收用户输入的用户名和密码字段信息,然后交给后台处理。Action变量指定了该表达式的提交方式:/a/login所对应的函数来处理。

    sysLogin.jsp

    <form id="loginForm" action="${ctx}/login" method="post">

    账号和密码的属性

    <div class="input-row">

        <label for="username">账号</label>

        <input type="text" name="username" id="username" placeholder="请填写登录账号">

    </div>

    <div class="input-row">

        <label for="password">密码</label>

        <input type="password" name="password" id="password" placeholder="请填写登录密码">

    </div>

    一个username一个password,表单会借由request属性传到函数种,到时候可以通过getUsername和getPassword两个函数从request中取出。但是简单之处必有难点出现。如何对shiro应用确实不易。

    LoginController.java控制层的方法

    /**

     * 管理登录

     */

    @RequestMapping(value = "${adminPath}/login", method = RequestMethod.GET)

    public String login(HttpServletRequest request, HttpServletResponse response, Model model) {

    Principal principal = UserUtils.getPrincipal();

    if (logger.isDebugEnabled()){

    logger.debug("login, active session size: {}", sessionDAO.getActiveSessions(false).size());

    }

    // 如果已登录,再次访问主页,则退出原账号。

    if (Global.TRUE.equals(Global.getConfig("notAllowRefreshIndex"))){

    CookieUtils.setCookie(response, "LOGINED", "false");

    }

    // 如果已经登录,则跳转到管理首页

    if(principal != null && !principal.isMobileLogin()){

    return "redirect:" + adminPath;

    }

    return "modules/sys/sysLogin";

    }

    /**

     * 登录失败,真正登录的POST请求由Filter完成

     */

    @RequestMapping(value = "${adminPath}/login", method = RequestMethod.POST)

    public String loginFail(HttpServletRequest request, HttpServletResponse response, Model model) {

    Principal principal = UserUtils.getPrincipal();

    // 如果已经登录,则跳转到管理首页

    if(principal != null){

    return "redirect:" + adminPath;

    }

    String username = WebUtils.getCleanParam(request, FormAuthenticationFilter.DEFAULT_USERNAME_PARAM);

    boolean rememberMe = WebUtils.isTrue(request, FormAuthenticationFilter.DEFAULT_REMEMBER_ME_PARAM);

    boolean mobile = WebUtils.isTrue(request, FormAuthenticationFilter.DEFAULT_MOBILE_PARAM);

    String exception = (String)request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);

    String message = (String)request.getAttribute(FormAuthenticationFilter.DEFAULT_MESSAGE_PARAM);

    if (StringUtils.isBlank(message) || StringUtils.equals(message, "null")){

    message = "用户或密码错误, 请重试.";

    }

    model.addAttribute(FormAuthenticationFilter.DEFAULT_USERNAME_PARAM, username);

    model.addAttribute(FormAuthenticationFilter.DEFAULT_REMEMBER_ME_PARAM, rememberMe);

    model.addAttribute(FormAuthenticationFilter.DEFAULT_MOBILE_PARAM, mobile);

    model.addAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME, exception);

    model.addAttribute(FormAuthenticationFilter.DEFAULT_MESSAGE_PARAM, message);

    if (logger.isDebugEnabled()){

    logger.debug("login fail, active session size: {}, message: {}, exception: {}",

    sessionDAO.getActiveSessions(false).size(), message, exception);

    }

    // 非授权异常,登录失败,验证码加1。

    if (!UnauthorizedException.class.getName().equals(exception)){

    model.addAttribute("isValidateCodeLogin", isValidateCodeLogin(username, true, false));

    }

    // 验证失败清空验证码

    request.getSession().setAttribute(ValidateCodeServlet.VALIDATE_CODE, IdGen.uuid());

    // 如果是手机登录,则返回JSON字符串

    if (mobile){

            return renderString(response, model);

    }

    return "modules/sys/sysLogin";

    }

    我们看到controller是负责接收前台数据,前台from中指定的是/a/login所以定位到相应的controller。细看这俩,只是简单的检查与跳转。这是因为shiro的登陆功能在controller之前加入了一个filter.这个filter被配置在文件Spring-context-shiro.xml文件中。

    <!-- Shiro权限过滤过滤器定义 -->

    <bean name="shiroFilterChainDefinitions" class="java.lang.String">

    <constructor-arg>

    <value>

    /static/** = anon

    /userfiles/** = anon

    ${adminPath}/cas = cas

    ${adminPath}/login = authc

    ${adminPath}/logout = logout

    ${adminPath}/** = user

    /act/editor/** = user

    /ReportServer/** = user

    </value>

    </constructor-arg>

    </bean>

    <!-- 安全认证过滤器 -->

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">

    <property name="securityManager" ref="securityManager" />

    <!--

    <property name="loginUrl" value="${cas.server.url}?service=${cas.project.url}${adminPath}/cas" /> -->

    <property name="loginUrl" value="${adminPath}/login" />

    <property name="successUrl" value="${adminPath}?login" />

    <property name="filters">

                <map>

                    <entry key="cas" value-ref="casFilter"/>

                    <entry key="authc" value-ref="formAuthenticationFilter"/>

                </map>

            </property>

    <property name="filterChainDefinitions">

    <ref bean="shiroFilterChainDefinitions"/>

    </property>

    </bean>

    最关键的部分。loginUrl属性所指定的url表示的是所有未通过验证的url所访问的位置。此处就是登陆界面了。successUrl表示成功登陆访问的url位置,也就是主页。Filters是配置具体验证方法的位置。在此处,${adminPath}/login = authc指定了/a/login,登陆页面所需要的验证权限名为authc.并且authc的filter也设置了,在map中:

    <entry key="authc" value-ref="formAuthenticationFilter"/>

     再来看formAuthenticationFilter中的处理,需要关注的类主要在com.thinkgem.jeesite.modules.sys.security这个包里。通常FormAuthenticationFilter是主要逻辑管理类,SystemAuthorizingRealm这个类则是数据处理类,相当于DAO。

    但是并未发现其功能,是因为这俩类都继承于shiro的类。

    总得讲,首先request被formAuthenticationFilter接收到,然后传给createToken函数,该函数从request中取出name and  password,然后生成自定义的一个token传给了SystemAuthorizingRealm中的doGetAuthenticationInfo验证。其中SystemAuthorizingRealm内有systemService的实例,该实例含有userDAO能取出数据库中的name and password 接着由这俩密码生成SimpleAuthenticationInfo,再由info中的逻辑来验证。

     protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {

        String username = getUsername(request);

        String password = getPassword(request);

        if (password==null){

            password = "";

        }

        boolean rememberMe = isRememberMe(request);

        String host = getHost(request);

        String captcha = getCaptcha(request);

        return new UsernamePasswordToken(username, password.toCharArray(), rememberMe, host, captcha);

    }

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {

        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;

         

        if (LoginController.isValidateCodeLogin(token.getUsername(), false, false)){

            // 判断验证码

            Session session = SecurityUtils.getSubject().getSession();

            String code = (String)session.getAttribute(ValidateCodeServlet.VALIDATE_CODE);

            if (token.getCaptcha() == null || !token.getCaptcha().toUpperCase().equals(code)){

                throw new CaptchaException("验证码错误.");

            }

        }

        User user = getSystemService().getUserByLoginName(token.getUsername());

        if (user != null) {

            byte[] salt = Encodes.decodeHex(user.getPassword().substring(0,16));

            return new SimpleAuthenticationInfo(new Principal(user),

                    user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName());

        } else {

            return null;

        }

    }

    之后就是service+dao+entity.

     

     

     

    Jeesite中的shiro

    引入链接:http://www.nohup.cc/article/23/

    2.1 spring-context-shiro.xml

        spring-context-shiro.xml是shiro的主配置文件,配置信息是重点主要是安全认证过滤器的配置。它规定哪些url需要进行哪些方面的认证和过滤

    <!-- 安全认证过滤器 -->

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">

        <property name="securityManager" ref="securityManager" />

        <property name="p" value="${adminPath}/login" />

        <property name="successUrl" value="${adminPath}" />

        <property name="filters">

            <map>

                <entry key="authc" value-ref="formAuthenticationFilter"/>

            </map>

        </property>

        <property name="filterChainDefinitions">

            <value>

                /static/** = anon

                /userfiles/** = anon

                ${adminPath}/login = authc

                ${adminPath}/logout = logout

                ${adminPath}/** = user

            </value>

        </property>

    </bean>

      shiroFilter是shiro的安全认证过滤器,其中,

    securityManager:指定一个负责管理的bean,这个新的bean在接下来会定义,其中包含了认证的主要逻辑。

    • loginUrl:没有登录的用户请求需要登录的页面时自动跳转到登录页面,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的”/login.jsp”页面。
    • successUrl:登录成功默认跳转页面,不配置则跳转至”/”。如果登陆前点击的一个需要登录的页面,则在登录自动跳转到那个需要登录的页面。不跳转到此。
    • unauthorizedUrl:没有权限默认跳转的页面。
    • map中的entry指定了authc权限所对应的过滤器实体

    而属性中的filterChainDefinitions则详细规定不同的url的对应权限

    • anon:例子/admins/**=anon 没有参数,表示可以匿名使用。
    • authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数
    • roles:例子/admins/user/=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
    • perms:例子/admins/user/**=perms[user:add:],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/=perms["user:add:,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
    • rest:例子/admins/user/=rest[user],根据请求的方法,相当于/admins/user/=perms[user:method] ,其中method为post,get,delete等。
    • port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString
      是你访问的url里的?后面的参数。
    • authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证
    • ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
    • user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
    • 注:anon,authcBasic,auchc,user是认证过滤器,perms,roles,ssl,rest,port是授权过滤器

    <!-- 定义 Shiro 主要业务对象 -->

    <bean id="securityManager"

    class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

    <!-- <property name="sessionManager" ref="sessionManager" /> -->

    <property name="realm" ref="systemAuthorizingRealm" />

    <property name="cacheManager" ref="shiroCacheManager" />

    </bean>

        这部分代码定义了securitymanager的主要属性的实体,systemAuthorizingRealm和shiroCacheManager都是自己实现的,分别用于进行验证管理和cache管理。从配置文件能看出,配置文件指定了作为安全逻辑的几个结构,除了这两部分,还包括formAuthenticationFilter。其中realm和filter在com.thinkgem.jeesite.modules.sys.security包里实现。

    2.2 FormAuthenticationFilter

        从可见都逻辑上讲,FormAuthenticationFilter类是用户验证时所接触的第一个类。
        当用户登录任意界面时,shiro会对当前状态进行检查。如果发现需要登录,则会自动跳转到配置文件里loginUrl属性所指定的url中。
        而这一url又被指定为authc权限,即需要验证。接着,authc的filter被指定为formAuthenticationFilter,因此login页面所提交的信息被改filter截获进行处理。
        其中的核心逻辑是createToken函数:

    protected AuthenticationToken createToken(S2ervletRequest request, ServletResponse response) {

        String username = getUsername(request);

        String password = getPassword(request);

        if (password==null){

            password = "";

        }

        boolean rememberMe = isRememberMe(request);

        String host = getHost(request);

        String captcha = getCaptcha(request);

        return new UsernamePasswordToken(username, password.toCharArray(), rememberMe, host, captcha);

    }

        UsernamePasswordToken是security包里的第四个类,它继承自shiro的同名类,用于shiro处理中的参数传递。createtoken函数接受到login网页所接受的表单,生成一个token传给下一个类处理。
        函数中的rememberme以及captcha分别表示的是记住用户功能和验证码功能,这部分也是shiro自身携带,我们并不需要修改。filter是通过aop的方式结合到系统里的,因此并没有具体的接口实现。

    2.3 SystemAuthorizingRealm

        shiro的最终处理都将交给Real进行处理。因为在Shiro中,最终是通过Realm来获取应用程序中的用户、角色及权限信息的。通常情况下,在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说,Realm是专用于安全框架的DAO.
    Realm中有个参数是systemService,这个便是spring的具体业务逻辑,其中也包含了具体的DAO,正是在这个部分,shiro与spring的接口具体的结合了起来。
    realm当中有两个函数特别重要,分别是用户认证函数和授权函数。

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {

        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;

         

        if (LoginController.isValidateCodeLogin(token.getUsername(), false, false)){

            // 判断验证码

            Session session = SecurityUtils.getSubject().getSession();

            String code = (String)session.getAttribute(ValidateCodeServlet.VALIDATE_CODE);

            if (token.getCaptcha() == null || !token.getCaptcha().toUpperCase().equals(code)){

                throw new CaptchaException("验证码错误.");

            }

        }

        User user = getSystemService().getUserByLoginName(token.getUsername());

        if (user != null) {

            byte[] salt = Encodes.decodeHex(user.getPassword().substring(0,16));

            return new SimpleAuthenticationInfo(new Principal(user),

                    user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName());

        } else {

            return null; }

        以上便是用户认证函数,中间关于验证码检测的部分我们暂且不表,其最核心的语句是

    User user = getSystemService().getUserByLoginName(token.getUsername());

    以及另一句语句

    return new SimpleAuthenticationInfo(new Principal(user), user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName());

        函数的参数AuthenticationToken便是filter所截获并生成的token实例,其内容是login表单里的内容,通常是用户名和密码。在前一句话里,getSystemService取到了systemService实例,该实例中又含有配置好的userDAO,能够直接与数据库交互。因此将用户id传入,取出数据库里所存有的user实例,接着在SimpleAuthenticationInfo生成过程中分别以user实例的用户名密码,以及token里的用户名密码做为参数,验证密码是否相同,以达到验证的目的。
        doGetAuthorizationInfo函数是授权的函数,其具体的权限是在实现类中以annotation的形式指派的,它负责验证用户是否有权限访问。详细的细节容我之后添加。

    2.3 LoginController

         LoginController是本该实现登录认证的部分。由于shiro的引入和AOP的使用,jeesite中的LoginController只处理验证之后的部分。
    如果通过验证,系统中存在user实例,则返回对应的主页。否则重新定位于login页面。

    2.4 annotation

        常用的annotation主要如下:

    @RequiresAuthentication
        要求当前Subject 已经在当前的session 中被验证通过才能被注解的类/实例/方法访问或调用。
        验证用户是否登录,等同于方法subject.isAuthenticated() 结果为true时。

    @RequiresUser
        需要当前的Subject 是一个应用程序用户才能被注解的类/实例/方法访问或调用。要么是通过验证被确认,或者在之前session 中的'RememberMe'服务被记住。
        验证用户是否被记忆,user有两种含义:一种是成功登录的(subject.isAuthenticated() 结果为true);另外一种是被记忆的(subject.isRemembered()结果为true)。

    @RequiresGuest
        要求当前的Subject 是一个“guest”,也就是他们必须是在之前的session中没有被验证或记住才能被注解的类/实例/方法访问或调用。
        验证是否是一个guest的请求,与@RequiresUser完全相反。
        换言之,RequiresUser == !RequiresGuest。此时subject.getPrincipal() 结果为null.

    @RequiresRoles
        要求当前的Subject 拥有所有指定的角色。如果他们没有,则该方法将不会被执行,而且AuthorizationException 异常将会被抛出。例如:@RequiresRoles("administrator")
    或者@RequiresRoles("aRoleName");
    void someMethod();
        如果subject中有aRoleName角色才可以访问方法someMethod。如果没有这个权限则会抛出异常AuthorizationException。

    @RequiresPermissions
        要求当前的Subject 被允许一个或多个权限,以便执行注解的方法,比如:
        @RequiresPermissions("account:create")
        或者@RequiresPermissions({"file:read", "write:aFile.txt"} )
        void someMethod();

     

     

    Alert(id)

    $.ajax({

       type:post,

       url:${pageContext.request.contextPage.},

       data:cid=+id,

       Success:function(data){

    alert(data)

    }

    });

    非常感谢各位查看我的随笔。 这些随笔是在生活中遇到的一些问题和解决方案或者是相关的知识。 希望对大家会有一些帮助。 如果有帮到各位的地方,希望可以对作者进行一定的捐助,谢谢。 https://files.cnblogs.com/files/zz-blog/zhifubao.bmp
  • 相关阅读:
    Android ADB 基本命令
    Android Studio修改包名
    Android开发之SurfaceView
    Android呼吸灯效果实现
    Android xml 绘制图形
    设计模式--代理模式C++实现
    设计模式--建造者模式C++实现
    设计模式--模板方法模式C++实现
    设计模式--抽象工厂模式C++实现
    设计模式---工厂方法C++实现
  • 原文地址:https://www.cnblogs.com/zz-blog/p/8377264.html
Copyright © 2020-2023  润新知