您正在查看: 2014年12月

项目笔记

一、shiro访问权限
1.如何规定访问权限?
@RequiresPermissions
参数是否只是简单的字符串匹配?(未解决)
2.如何指定对应用户的访问权限?
(1)doGetAuthorizationInfo中有getMenuList()函数,该函数获取当前user所拥有的menu。
1当前user何时指定?
在doGetAuthenticationInfo中生成Principal(user)。
从此就能通过subject.getPrincipal取出,也能在doGetAuthorizationInfo当参数中。
(2)接着通过addStringPermission添加到subject里
(3)当@RequriesPerminssions时从subject中取出验证

二、cache
1.在UserUtils中

三、page

四、取rolelist和menulist

1.各种hibernate的annotation
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "sys_role_menu", joinColumns = { @JoinColumn(name = "role_id") }, inverseJoinColumns = { @JoinColumn(name = "menu_id") })
@Where(clause="del_flag='"+DEL_FLAG_NORMAL+"'")
@OrderBy("id") @Fetch(FetchMode.SUBSELECT)
@NotFound(action = NotFoundAction.IGNORE)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)

五、aop
六、多个filter或realm
//1不同url对应不同的realm??
2定义两个shirofilter

七、log日志
1.LogInterceptor中有简单的日志记录

Maven中自动生成关于项目站点的一些坑

Overview

主要使用使用Maven自动生成项目站点的一些坑。

List

  • maven-site-plugin 插件需要使用3.3版本,如果使用3.2,由于兼容性问题可能在site阶段报错。

  • Eclipse中启动mvn site阶段找不到JAVA_HOME:
    报错信息如下:

     Failed to execute goal org.apache.maven.plugins:maven-javadoc-plugin:2.9.1:jar (attach-javadocs) on project suppress-warnings: MavenReportException: Error while creating archive: Unable to find javadoc command: The environment variable JAVA_HOME is not correctly set.
    

    已经在本地的配置中设置了JAVA_HOME

     export JAVA_HOME=$(/usr/libexec/java_home)
    

    在命令行运行 mvn clean site:sitejavadoc 可以找到JAVA_HOME,但是在eclipse中执行maven构建,还是会报一样的错误,这是由于,在本地设置的JAVA_HOME不能被A bundled GUI app like Eclipse识别,需要在全局配置中添加该信息。
    找到/etc/launchd.conf文件,如果该目录不存在这个文件,sudo vim launchd.conf创建改文件,必须使用sudo,不然无法写入该文件,添加一行:

     setenv JAVA_HOME /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home
    

    注意:不能使用

     setenv JAVA_HOME $(/usr/libexec/java_home) 
    

    这样的小技巧,否则,这里不能识别这种写法。

    保存,并重启电脑,配置生效。

jeesite里spring-mvc的初步认识

1.整体框架和流程

    首先是跟spring-mvc没有直接关系但是是系统整体的流程的说明。
    以user管理为例,最外层是UserController结构,这个结构是跟spring-mvc直接关联的,之后会细讲。这个结构的主要是分配url的处理函数。当用户在浏览器中输入url时,spring-mvc把url和表单信息引导到UserController的某个函数中处理,然后返回对应的view表示物。该view有多种返回形式。权限验证都在这一层,能够判断哪些view有权限访问。
    controller中有一个SystemService,基本就是传统ssh框架中的service了,负责某一功能的业务逻辑实现。service中还有各种DAO的实体,能够通过DAO访问数据库。除此之外,还有SystemAuthorizingRealm以及IdentityService,前者能在执行处理的过程中管理认证信息,特别是一些缓存信息;后者则跟Activiti,也就是工作流有关,具体细节还待摸索。总而言之,service直接操作数据实体,提供所需的各种业务逻辑实现。这些相关类都通过spring的自动装配导入。Transaction都在这一层。
    此外就是User类和UserDAO,hibernate的内容,不用赘述。

2.spring-mvc的annotation

2.1 annotaton

@Autowired:
    @Autowired注解标签可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作。可以写在成员变量之上,也可以在变量对应的set方法上。

public class RoleServicesLogic implements RoleServices {
    @Autowired
    private RoleDao roleDao; 
}

    也可以对方法进行注解

public class RoleServicesLogic implements RoleServices {
    private RoleDao roleDao; 
    @Autowired
    public void setRoleDao(RoleDao roleDao) {
        this.roleDao = roleDao;
    } 
}

    以上两种不同实现方式中,@Autowired的标注位置不同,它们都会在Spring在初始化RoleManagerImpl这个bean时, 自动装配roleDao这个属性,区别是:第一种实现中,Spring会直接将RoleDao类型的唯一一个bean赋值给roleDao这个成员变量; 第二种实现中,Spring会调用setRoleDao方法来将RoleDao类型的唯一一个bean装配到RoleDao这个属性。

2.2 常用spring的anootation

  • @Service用于标注业务层组件
  • @Repository用于标注数据访问组件,即DAO组件
  • @Controller用于标注控制层组件,如Struts中的Action或者springmvc的controller
  • @Component泛指组件,当组件不好归类时,可以使用这个注解进行标注
  • @Entity 实体类注解标签
  • @Table 实体类对应的数据库实际的表名,不设置表名默认数据库表名与类名一致。

2.3 RequestMapping

1)最基本的,方法级别上应用,例如:
@RequestMapping(value="/departments")  
public String simplePattern(){   
    System.out.println("simplePattern method was called");  
    return "someResult";  
}  

    则访问http://localhost/xxxx/departments的时候,会调用 simplePattern方法了

2)参数绑定
@RequestMapping(value="/departments")  
public String findDepatment(  
    @RequestParam("departmentId") String departmentId){       
    System.out.println("Find department with ID: " + departmentId);  
    return "someResult";  
}  

    形如这样的访问形式:
    /departments?departmentId=23就可以触发访问findDepatment方法了

3) REST风格的参数
@RequestMapping(value="/departments/{departmentId}")  
public String findDepatment(@PathVariable String departmentId){  
    System.out.println("Find department with ID: " + departmentId);  
    return "someResult";  
}  

    形如REST风格的地址访问,比如:
    /departments/23,其中用(@PathVariable接收rest风格的参数

4) REST风格的参数绑定形式之2

    先看例子,这个有点象之前的:

@RequestMapping(value="/departments/{departmentId}")  
public String findDepatmentAlternative(  
    @PathVariable("departmentId") String someDepartmentId){  
    System.out.println("Find department with ID: " + someDepartmentId);  
    return "someResult";  
}  

    这个有点不同,就是接收形如/departments/23的URL访问,把23作为传入的departmetnId,,但是在实际的方法findDepatmentAlternative中,使用
@PathVariable("departmentId") String someDepartmentId,将其绑定为
someDepartmentId,所以这里someDepartmentId为23

5) url中同时绑定多个id
   @RequestMapping(value="/departments/{departmentId}/employees/{employeeId}")  
public String findEmployee(  
    @PathVariable String departmentId,  
    @PathVariable String employeeId){  
    System.out.println("Find employee with ID: " + employeeId +   " from department: " + departmentId);  
    return "someResult";  
}  

    这个其实也比较好理解了。

6) 支持正则表达式
@RequestMapping(value="/{textualPart:[a-z-]+}.{numericPart:[\\d]+}")  
public String regularExpression(  
    @PathVariable String textualPart,  
    @PathVariable String numericPart){  
    System.out.println("Textual part: " + textualPart +   ", numeric part: " + numericPart);  
    return "someResult";  
}  

    比如如下的URL:/sometext.123,则输出:
    Textual part: sometext, numeric part: 123

2.4 RequestParam

    springmvc提供了@RequestParam注释帮助我们获取参数。
    用法@RequestParam("接收的参数名")

@RequestMapping(params="servlet=login")
public String login(@RequestParam("username")String username,@RequestParam("password")String password,HttpServletRequest request,ModelMap map){
     //处理登录逻辑,省略
    return "success";
}

    当客户端的URL提交了username参数,password参数,那么我们的Controller就可以接收并处理了。
    要注意,提交的username参数和password参数不可以是null,即一定要传这两个参数,不然会抛异常。
    另外,@RequestParam可以省略参数名,那么就会以它注释的变量名作为参数名。

2.5 ModelAttribute

    @ModelAttribute一个具有如下三个作用:
    ①绑定请求参数到命令对象:放在功能处理方法的入参上时,用于将多个请求参数绑定到一个命令对象,从而简化绑定流程,而且自动暴露为模型数据用于视图页面展示时使用;

    ②暴露表单引用对象为模型数据:放在处理器的一般方法(非功能处理方法)上时,是为表单准备要展示的表单引用对象,如注册时需要选择的所在城市等,而且在执行功能处理方法(@RequestMapping注解的方法)之前,自动添加到模型对象中,用于视图页面展示时使用;

    ③暴露@RequestMapping方法返回值为模型数据:放在功能处理方法的返回值上时,是暴露功能处理方法的返回值为模型数据,用于视图页面展示时使用。

2.5.1 绑定请求参数到命令对象

    如用户登录,我们需要捕获用户登录的请求参数(用户名、密码)并封装为用户对象,此时我们可以使用@ModelAttribute绑定多个请求参数到我们的命令对象。

public String test1(@ModelAttribute("user") UserModel user)

    只是此处多了一个注解@ModelAttribute("user"),它的作用是将该绑定的命令对象以“user”为名称添加到模型对象中供视图页面展示使用。我们此时可以在视图页面使用${user.username}来获取绑定的命令对象的属性。

    绑定请求参数到命令对象支持对象图导航式的绑定,如请求参数包含“?username=zhang&password=123&workInfo.city=bj”自动绑定到user中的workInfo属性的city属性中。

@RequestMapping(value="/model2/{username}")
public String test2(@ModelAttribute("model") DataBinderTestModel model) { 

    URI模板变量也能自动绑定到命令对象中,当你请求的URL中包含“bool=yes&schooInfo.specialty=computer&hobbyList[

2.5.2 暴露表单引用对象为模型数据
@ModelAttribute("cityList")
public List<String> cityList() {
    return Arrays.asList("北京", "山东");
} 

    如上代码会在执行功能处理方法之前执行,并将其自动添加到模型对象中,在功能处理方法中调用Model 入参的containsAttribute("cityList")将会返回true。

@ModelAttribute("user")  //①
public UserModel getUser(@RequestParam(value="username", defaultValue="") String username) {
    //TODO 去数据库根据用户名查找用户对象
    UserModel user = new UserModel();
    user.setRealname("zhang");
     return user;
} 

    如你要修改用户资料时一般需要根据用户的编号/用户名查找用户来进行编辑,此时可以通过如上代码查找要编辑的用户。也可以进行一些默认值的处理。

@RequestMapping(value="/model1") //②
public String test1(@ModelAttribute("user") UserModel user, Model model) 

    此处我们看到①和②有同名的命令对象,那Spring Web MVC内部如何处理的呢:

  • 首先执行@ModelAttribute注解的方法,准备视图展示时所需要的模型数据;@ModelAttribute注解方法形式参数规则和@RequestMapping规则一样,如可以有@RequestParam等;
  • 执行@RequestMapping注解方法,进行模型绑定时首先查找模型数据中是否含有同名对象,如果有直接使用,如果没有通过反射创建一个,因此②处的user将使用①处返回的命令对象。即②处的user等于①处的user。
2.5.3 暴露@RequestMapping方法返回值为模型数据
public @ModelAttribute("user2") UserModel test3(@ModelAttribute("user2") UserModel user)

    大家可以看到返回值类型是命令对象类型,而且通过@ModelAttribute("user2")注解,此时会暴露返回值到模型数据(名字为user2)中供视图展示使用。那哪个视图应该展示呢?此时Spring Web MVC会根据RequestToViewNameTranslator进行逻辑视图名的翻译。
    此时又有问题了,@RequestMapping注解方法的入参user暴露到模型数据中的名字也是user2,其实我们能猜到:
    @ModelAttribute注解的返回值会覆盖@RequestMapping注解方法中的@ModelAttribute注解的同名命令对象。

2.5.4 匿名绑定命令参数
public String test4(@ModelAttribute UserModel user, Model model)
或
public String test5(UserModel user, Model model) 

    此时我们没有为命令对象提供暴露到模型数据中的名字,此时的名字是什么呢?Spring Web MVC自动将简单类名(首字母小写)作为名字暴露,如“cn.javass.chapter6.model.UserModel”暴露的名字为“userModel”。

public @ModelAttribute List<String> test6()
或
public @ModelAttribute List<UserModel> test7() 

    对于集合类型(Collection接口的实现者们,包括数组),生成的模型对象属性名为“简单类名(首字母小写)”+“List”,如List生成的模型对象属性名为“stringList”,List生成的模型对象属性名为“userModelList”。
    其他情况一律都是使用简单类名(首字母小写)作为模型对象属性名,如Map<String, UserModel>类型的模型对象属性名为“map”。

3.前后台交互

3.1 通过注释ModelAttribute来进行。这部分已经在上文介绍过。

3.2 方法返回参数

    在低版本的 Spring MVC 中,请求处理方法的返回值类型都必须是 ModelAndView。而在 Spring 2.5 中,你拥有多种灵活的选择。通过下表进行说明:

void:

此时逻辑视图名由请求处理方法对应的 URL 确定,如以下的方法:
@RequestMapping("/welcome.do")
public void welcomeHandler() {
}
对应的逻辑视图名为“welcome”

String:

此时逻辑视图名为返回的字符,如以下的方法:
@RequestMapping(method = RequestMethod.GET)
public String setupForm(@RequestParam("ownerId") int ownerId, ModelMap model) {
    Owner owner = this.clinic.loadOwner(ownerId);
    model.addAttribute(owner);
    return "ownerForm";
}
对应的逻辑视图名为“ownerForm”

org.springframework.ui.ModelMap:

和返回类型为 void 一样,逻辑视图名取决于对应请求的 URL,如下面的例子:
@RequestMapping("/vets.do")
public ModelMap vetsHandler() {
    return new ModelMap(this.clinic.getVets());
}
对应的逻辑视图名为“vets”,返回的 ModelMap 将被作为请求对应的模型对象,可以在 JSP 视图页面中访问到。

ModelAndView:

应该说使用 String 作为请求处理方法的返回值类型是比较通用的方法,这样返回的逻辑视图名不会和请求 URL 绑定,具有很大的灵活性,而模型数据又可以通过 ModelMap 控制。当然直接使用传统的 ModelAndView 也不失为一个好的选择。

3.3 按契约绑定URL 参数

Controller 的方法标注了 @RequestMapping 注解后,它就能处理特定的 URL 请求。我们不禁要问:请求处理方法入参是如何绑定 URL 参数的呢?在回答这个问题之前先来看下面的代码:

@RequestMapping(params = "method=listBoardTopic")
//<—— ① topicId入参是如何绑定URL请求参数的?
public String listBoardTopic(int topicId) { 
    bbtForumService.getBoardTopics(topicId);
    System.out.println("call listBoardTopic method.");
    return "listTopic";
}

    当我们发送 http://localhost//bbtForum.do?method=listBoardTopic&topicId=10 的 URL 请求时,Spring 不但让 listBoardTopic() 方法处理这个请求,而且还将 topicId 请求参数在类型转换后绑定到 listBoardTopic() 方法的 topicId 入参上。而 listBoardTopic() 方法的返回类型是 String,它将被解析为逻辑视图的名称。也就是说 Spring 在如何给处理方法入参自动赋值以及如何将处理方法返回值转化为 ModelAndView 中的过程中存在一套潜在的规则,不熟悉这个规则就不可能很好地开发基于注解的请求处理方法,因此了解这个潜在规则无疑成为理解 Spring MVC 框架基于注解功能的核心问题。
    我们不妨从最常见的开始说起:请求处理方法入参的类型可以是 Java 基本数据类型或 String 类型,这时方法入参按参数名匹配的原则绑定到 URL 请求参数,同时还自动完成 String 类型的 URL 请求参数到请求处理方法参数类型的转换。下面给出几个例子:

listBoardTopic(int topicId):和 topicId URL 请求参数绑定;
listBoardTopic(int topicId,String boardName):分别和 topicId、boardName URL 请求参数绑定;

    特别的,如果入参是基本数据类型(如 int、long、float 等),URL 请求参数中一定要有对应的参数,否则将抛出 TypeMismatchException 异常,提示无法将 null 转换为基本数据类型。
    另外,请求处理方法的入参也可以一个 JavaBean,如下面的 User 对象就可以作为一个入参:

User.java:一个 JavaBean
            
package com.baobaotao.web;

    public class User {
    private int userId;
    private String userName;
    //省略get/setter方法
    public String toString(){
        return this.userName +","+this.userId;
    }
}

下面是将 User 作为 listBoardTopic() 请求处理方法的入参:

使用 JavaBean 作为请求处理方法的入参

@RequestMapping(params = "method=listBoardTopic")
public String listBoardTopic(int topicId,User user) {
    bbtForumService.getBoardTopics(topicId);
    System.out.println("topicId:"+topicId);
    System.out.println("user:"+user);
    System.out.println("call listBoardTopic method.");
    return "listTopic";
}

    这时,如果我们使用以下的 URL 请求:http://localhost/bbtForum.do?method=listBoardTopic&topicId=1&userId=10&userName=tom
    topicId URL 参数将绑定到 topicId 入参上,而 userId 和 userName URL 参数将绑定到 user 对象的 userId 和 userName 属性中。和 URL 请求中不允许没有 topicId 参数不同,虽然 User 的 userId 属性的类型是基本数据类型,但如果 URL 中不存在 userId 参数,Spring 也不会报错,此时 user.userId 值为 0。如果 User 对象拥有一个 dept.deptId 的级联属性,那么它将和 dept.deptId URL 参数绑定。

3.3 通过注解指定绑定 URL 参数

    如果我们想改变这种默认的按名称匹配的策略,比如让 listBoardTopic(int topicId,User user) 中的 topicId 绑定到 id 这个 URL 参数,那么可以通过对入参使用 @RequestParam 注解来达到目的:

package com.baobaotao.web;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/bbtForum.do")
public class BbtForumController {

    @RequestMapping(params = "method=listBoardTopic")
    public String listBoardTopic(@RequestParam("id") int topicId,User user) {
        bbtForumService.getBoardTopics(topicId);
        System.out.println("topicId:"+topicId);
        System.out.println("user:"+user);
        System.out.println("call listBoardTopic method.");
        return "listTopic";
    }
}

    这里,对 listBoardTopic() 请求处理方法的 topicId 入参标注了 @RequestParam("id") 注解,所以它将和 id 的 URL 参数绑定。

3.4 绑定模型对象中某个属性

    Spring 2.0 定义了一个 org.springframework.ui.ModelMap 类,它作为通用的模型数据承载对象,传递数据供视图所用。我们可以在请求处理方法中声明一个 ModelMap 类型的入参,Spring 会将本次请求模型对象引用通过该入参传递进来,这样就可以在请求处理方法内部访问模型对象了。来看下面的例子:

使用 ModelMap 访问请示对应的隐含模型对象
            
@RequestMapping(params = "method=listBoardTopic")
 public String listBoardTopic(@RequestParam("id")int topicId,User user,ModelMap model) {
     bbtForumService.getBoardTopics(topicId);
     System.out.println("topicId:" + topicId);
     System.out.println("user:" + user);
     //① 将user对象以currUser为键放入到model中
     model.addAttribute("currUser",user); 
     return "listTopic";
 }

    对于当次请求所对应的模型对象来说,其所有属性都将存放到 request 的属性列表中。象上面的例子,ModelMap 中的 currUser 属性将放到 request 的属性列表中,所以可以在 JSP 视图页面中通过 request.getAttribute(“currUser”) 或者通过 ${currUser} EL 表达式访问模型对象中的 user 对象。从这个角度上看, ModelMap 相当于是一个向 request 属性列表中添加对象的一条管道,借由 ModelMap 对象的支持,我们可以在一个不依赖 Servlet API 的 Controller 中向 request 中添加属性。
    在默认情况下,ModelMap 中的属性作用域是 request 级别是,也就是说,当本次请求结束后,ModelMap 中的属性将销毁。如果希望在多个请求中共享 ModelMap 中的属性,必须将其属性转存到 session 中,这样 ModelMap 的属性才可以被跨请求访问。

Jeesite中shiro的用法讲解

前言

    Apache Shiro 是一个框架,可用于身份验证和授权。虽然这两个术语代表的是不同的含义,但出于它们在应用程序安全性方面各自的角色考虑,它们有时会被交换使用。
身份验证 指的是验证用户的身份。在验证用户身份时,需要确认用户的身份的确如他们所声称的那样。在大多数应用程序中,身份验证是通过用户名和密码的组合完成的。只要用户选择了他人很难猜到的密码,那么用户名和密码的组合通常就足以确立身份。但是,还有其他的身份验证方式可用,比如指纹、证书和生成键。
    一旦身份验证过程成功地建立起身份,授权 就会接管以便进行访问的限制或允许。 所以,有这样的可能性:用户虽然通过了身份验证可以登录到一个系统,但是未经过授权,不准做任何事情。还有一种可能是用户虽然具有了某种程度的授权,却并未经过身份验证。
在为应用程序规划安全性模型时,必须处理好这两个元素以确保系统具有足够的安全性。身份验证是应用程序常见的问题(特别是在只有用户和密码组合的情况下),所以让框架来处理这项工作是一个很好的做法。合理的框架可提供经过测试和维护的优势,让您可以集中精力处理业务问题,而不是解决其解决方案已经实现的问题。
    Apache Shiro 提供了一个可用的安全性框架,各种客户机都可将这个框架应用于它们的应用程序。本文中的这些例子旨在介绍 Shiro 并着重展示对用户进行身份验证的基本任务。
本文只针对Jeesite中shiro的用法进行整理,不会包括shiro环境配置和搭建等内容。

Jeesite中的shiro

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();

3.相关网页

    有一个简易的整体流程实现
http://blog.csdn.net/clj198606061111/article/details/24185023
    对授权部分解释较为详细
http://sishuok.com/forum/posts/list/7456.html

Maven中实现自动部署到Tomcat

Overview

以下纪录了实现Maven自动部署的步骤。

修改本地Tomcat端口

为了保持本地环境跟服务器环境一致,方便部署,首先需要修改本地tomcat的默认端口号,将8080改为8888:修改tomcat目录中/conf/server.xml中的Connectorport属性。

    <Connector port="8888" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443"
           URIEncoding="UTF-8"/>

修改settings.xml

  • 修改用户当前根目录下~/.m2/settings.xml文件,默认情况下,.m2目录下并不在settings.xml,需要手动把${MAVEN_HOME}/libexec/conf/settings.xml复制到~/.m2/下,比如在我的环境下,执行如下命令:
    cp /usr/local/Cellar/maven/3.2.3/libexec/conf/settings.xml ~/.m2
    有的maven版本位置为${MAVEN_HOME}/conf/settings.xml

  • 添加server信息
    <servers></servers>标签对中,添加如下信息

      <server>
        <id>mytomcat6</id>
        <username>admin</username>
        <password>admin</password>
      </server>
    

修改项目pom.xml

  • 在pom中添加plugin:

       <plugin>
              <groupId>org.codehaus.mojo</groupId>
              <artifactId>tomcat-maven-plugin</artifactId>
              
              <configuration>
                  <server>mytomcat6</server>
                  <update>true</update>
                  <port>${tomcat.port}</port>
                  <path>/${project.artifactId}</path>
                  <uriEncoding>${project.build.sourceEncoding}</uriEncoding>
                  <url>http://localhost:8888/manager</url>
              </configuration>
          </plugin>
    

修改tomcat下的tomcat-users.xml文件

  • 在tomcat文件夹中找到/conf/tomcat-users.xml,比如我的文件地址为:/Library/apache-tomcat-6.0.43/conf/tomcat-users.xml

  • 在tomcat-users.xml中<users></users>对中添加如下信息:

      <role rolename="manager-gui"/>
      <role rolename="manager-script"/>
      <role rolename="manager-jmx"/>
      <user username="admin" password="admin" roles="manager-gui,manager-script,manager-jmx"/>
    

自动部署到tomcat

  • 使用 mvn clean tomcat:deploy 命令部署。注意,不是mvn clean deploy命令,deploy命令包含了install命令,会先将项目构件安装到本地maven库中,然后安装到远程配置好的私有仓库上。

  • 需要先启动tomcat,否则会有如下报错信息:

      [ERROR]Failed to execute goal org.apache.tomcat.maven: tomcat7-maven-plugin: 2.0- SNAPSHOT: deploy (default-cli) on project helloworld: Cannot invoke Tomcat manager: Connection refused: connect -> [Help 1]
    

还差一点

此时部署,会报以下错误:

FAIL - Application already exists at path
  • 原因:

    由于tomcat的目录下:/Library/apache-tomcat-6.0.43/conf/server.xml有了重复配置的上下面:

      <Context docBase="jeesite" path="/jeesite_lian"                             reloadable="true" source="org.eclipse.jst.jee.server:jeesite"/>
    <Context docBase="jeesite_lian" path="/jeesite_lian" reloadable="true" source="org.eclipse.jst.jee.server:jeesite_lian"/>
    

path属性配置重复,由于之前有一个相同的本地项目,这次运行的项目是从SVN下载到本地的,但是拥有一样的path属性,因此报错。注释掉这两个Context就可以了。

支持热部署

需要在pomtomcat6插件的configuration配置项中添加一个属性, <update>true</update>,完整的插件如下

    <!-- tomcat6插件 -->
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat6-maven-plugin</artifactId>
            <version>${tomcat.version}</version> 
            <configuration>
                <server>mytomcat6</server>
                <update>true</update>
                <port>${webserver.port}</port>
                <path>/${project.artifactId}</path>
                <uriEncoding>${project.build.sourceEncoding}</uriEncoding>
                <url>http://localhost:8080/manager</url>
            </configuration>
        </plugin>

使用Hudson构建mvn clean tomcat:deploy 并部署到远程服务器Tomcat

  • 报错:JVM虚拟机内存不足,无法分配内存。

  • 解决方案:
    在Hudson设置界面,build选项栏下点击advance按钮
    1.png

    在JVM Options选项中增加-Xmx512m增加JVM虚拟机的内存大小。
    2.png

  • 小插曲
    在修改了JVM内存之后,发现依然报错。使用htop命令查看发现由于阿里云服务器内存太小(1G),导致实际内存被耗尽,Hudson使用的JVM内存无法向上调整。升级配置之后,问题解决。