您的位置:首页 > 编程语言 > Java开发

Spring mvc 3.0 入门及应用

2012-02-22 16:02 351 查看
【一】Spring应用

Spring支持json格式的jar

jackson-all-1.7.3http://jackson.codehaus.org/
SpringMVC3.xannotatedcontroller的几点心得体会

(最优化使用http://www.javaeye.com/topic/828513)

一、callback回调

templatemethod(模板方法设计模式)-hibernateTemplate

二、JPA2.0的配置

src/META-INF/persistence.xml

webContent/META-INF/context.xml

webContent/WEB-INF/applicationContext.xml

三、Hibernate配置

【二】spring3.0.5与hibernate3.6.1(JPA2.0)整合

【三】springMVC使用

SpringMVC核心流程示意图

DispatcherServlet--frontcontroller前端总控制器

Controller--应用开发的控制器,实现controller接口,

HandlerMapping--控制器的映射,控制controller策略

ViewResolver&View--视图策略,View是试图预处理

InternalResourceViewResolver--jsp和jstl的解析器,

Interceptors:拦截器

LocalResolver:国际化

Validate:验证

ClassNameHandlerMapping类名控制器--按类名匹配的abc.test的AbcController

BeanNameUrlHandlerMapping<beanname="/to_add.test"class=""/>

--------------------------------基于注解的MVC

基于注解的SpringMVC

一、@Controller-类上

@RequestMapping--修饰两种类型:方法上和类上

(1)类级别上:表示首先定义了相对的父路径,那么在方法上定义的路径是相对于类级别上的。

(2)方法上:如果类级别没有RequestMapping映射,则相对于应用,如果类级别有,则是相对于

类级别的注解的路径

二、路径变量:restful风格--PathVariable

http://localhost:8080/@mvc/test/start/zhangsan.do--请求的参数作为url的一部分来存在
http://localhost:8080/@mvc/test/start/lisi.do
@RequestMapping("/start/{name}/{age}")

publicStringstart(@PathVariable("name")Stringname,@PathVariable("age")int

nianling){//此处name与上面的name一致,

}

//此处注意:java编辑的debug模式和release模式

三、根据不同的请求方法(POST/GET),映射到不同的处理方法

@RequestMapping(value="/start",method=RequestMethod.GET)

publicStringPostStart(){/

}

@RequestMapping(value="/start",method=RequestMethod.POST)

publicStringstart(){/

}

四、数据绑定,注解中的实现

@RequestMapping(value="/{test}",method=RequestMethod.GET)

publicStringstart(){/

}

@Initbinder--初始化绑定器标注

publicvoidinitbinder(WebRequestDataBinderbinder,HttpServletRequestrequest){

binder.registerCustomEditor(Date.class,newCustomDateEditor(newSimpleDateFormat

("yyyy-MM-dd"),false));

}

五、处理请求的方法,接收的参数和返回的参数

1.接收的参数:数量与顺序没有要求

=

(1)没有参数()

(2)(@PathvariableanyType)

(3)(request,response,session,)

//session的前提,当前请求session可用。

(4)request参数@RequestParamanyType//--?id=45参数,通过类型转换器转换

(5)@CookieValueanyTypeCookieName//cookie值取出,通过类型转换

(6)@RequestHeader("user-Agent")AnyTypeabc--从请求的头部中取出正确的值,例如(user-

Agent,contentetc)

(7)经常调用out=response.getWriter()

@RequestMapping

publicvoidtestAllArguments(PrintWriterout){

//out=response.getWriter();

out.println();

}

(8)可以是Map

publicModelAndViewtest(Mapmodel){

//返回modelandview

model.put("","");

returnnewModelAndView("viewName");

}

(9)表单参数生成bean注入

publicStringtest(Useruser,BindingResultresult)

//user自动为command对象,直接注入,此处要进行数据绑定的处理(进行绑定器)

//BindingResult--处理错误绑定信息,判断是否绑定错误信息,进行处理,与数据验证有关

2.返回值类型

(1)void-直接输出结果,两种情况:

第一:有out对象输出:out.print

第二:没有out输出的话,会生成一个隐含的viewName-->按请求路径解析出的

${appname}/test/xxx.do-->/WEB-INF/page/test/xxx.jsp

(2)String类型:viewName

(3)任意类型AnyType对象--//model("user",user),user自动填充到模型中。试图名为默认解析

的名字

publicUsertestUser(){

}

(4)返回list--//model.("userList",list)

publicList<User>queryUsers(){

returnnull;

}

(5)Model,Map,ModelAndView只返回模型是,试图名按默认解析

六、重定向视图return"redirect:/usr_list"

@Controller

@RequestMammping("/user")

publcclassUserController{

@RequestMapping(method=RequestMethod.GET)

publicStringaddForm(){

return"add_user";

}

@RequestMapping(method=RequestMethod.POST)

publicStringaddUser(Useruser){

return"redirect:/user_list";//重定向视图,防止再次进行提交操作,重复提交

}

}

产生原因:request.forward();-带着参数,由服务器转到另外一个方法

response.sendRedirect(b)--重定向方法,不带参数转换,浏览器知道发生了改变,浏览器再次

发生一次请求b

解决方法:将结果重定向到另一个页面:return"redirect:success";

七、ExcelViewJsperReport,Pdf,XML视图

八、拦截器,视图的国际化,表单验证

1.拦截器:全局日志,权限管理(拦截器细粒度)

(1)定义一个拦截器,多个拦截器形成链:实现HanderInerceptor接口,三个方法

preHandler()

postHandler()

afterCompletion()

(2)配置拦截器:

<mvc:interceptors>

<mvc:interceptor>

<mvc:mappingpath="/usr/*"/>--可选

<beanclass="test.web.myInerceptor"/>

</mvc:interceptor>

</mvc:interceptors>

2.国际化(视图)--中文/英文/日文/韩文

引入spring标签库:

<%@tabliburi="http://www.springframework.org/tags"prefix="spring"%>

<div>

<spring:messagecode="welcome"/>--引用属性文件的welcome

</div>

配置资源文件

<beanid="resourceMessage(此名不可变)"

class="org.sf.context.support.resourceBundleMessageSource">

<propertyname="basename"value="mess"/>

<propertyname="basenames">

<value>...</value>

<value>...</value>

</property>

</bean>

3.验证Validate(Spring后端验证采用JSR-303validatebean标准,前断用javascript)

后端用@Valid注解--自动调用约束条件验证,条件在实体类中指定

比较的好的实现:hibernate-validator4.0

classUser

{

@NotEmpty

privatestringname

@Size(max=20,min=6)

privateStringpassword;

}

publicStringaddUser(@ValidUseruser,BindingResultresult){

}

九、对json的原生支持

springmvcajax使用json
http://liyixing1.iteye.com/blog/1098743

【四】spring与spring3.0.5_quartz1.8.4整合

(e:\openprojects\spring)

http://www.javaeye.com/topic/906313关于调度的框架

【五】spring3.0.5与zk5.0.5的集成详细说明

http://www.javaeye.com/topic/891134

【六】SpringSecurity配置

重要参考文献:http://sarin.javaeye.com/blog/829738

准备jia文件

一、基础配置

1.配置过滤器

1.<context-param>

2.<param-value>

3./WEB-INF/board-service.xml

4./WEB-INF/board-security.xml

5.</param-value>

6.</context-param>

1.<filter>

2.<filter-name>springSecurityFilterChain</filter-name>

3.<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

4.</filter>

5.<filter-mapping>

6.<filter-name>springSecurityFilterChain</filter-name>

7.<url-pattern>/*</url-pattern>

8.</filter-mapping>

过滤器配置:初始化参数和springscurity过滤器

2.SpringMVC的sevlet配置

1.<?xmlversion="1.0"encoding="UTF-8"?>

2.<beansxmlns="http://www.springframework.org/schema/beans"

3.xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

4.xmlns:security="http://www.springframework.org/schema/security"

5.xmlns:context="http://www.springframework.org/schema/context"

6.xsi:schemaLocation="http://www.springframework.org/schema/beans

7.http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
8.http://www.springframework.org/schema/context
9.http://www.springframework.org/schema/context/spring-context-3.0.xsd
10.http://www.springframework.org/schema/security
11.>'target='_blank'>http://www.springframework.org/schema/security/spring-security-3.0.xsd">
12.<context:component-scanbase-package="org.ourpioneer.board.web"/>

13.<bean

14.class="org.springframework.web.servlet.view.InternalResourceViewResolver">

15.<propertyname="prefix"value="/WEB-INF/jsp/"/>

16.<propertyname="suffix"value=".jsp"/>

17.</bean>

18.<security:global-method-security

19.jsr250-annotations="enabled"secured-annotations="enabled"/>

20.</beans>

以上配置springsecurity配置为支持注释形式。Springsecurity配置的文件,必须在springmvc初始化的配置文件中初始化,否则不起作用。也就是说secrrity的配置文件必须和springmvc的配置文件都被springMVC放在一起(web.xml配置springmvc时,init-para也要把security的放进去)框架加载。

3.配置自定义登录界面

1.<security:httpaccess-denied-page="/accessDenied.jsp">

2.<security:form-loginlogin-page="/login.jsp"

3.login-processing-url="/login"default-target-url="/messageList.htm"

4.authentication-failure-url="/login.jsp?error=true"/>

5.<security:logoutlogout-success-url="/login.jsp"/>

6.</security:http>

(1)login-page属性配置的是登录页面;access-denied-page:自定义访问拒绝(无权访问)页面。

(2)login-processing-url就是我们处理登录逻辑的请求地址,默认的是j_spring_security_check;

(3)default_target_url就是默认的登录成功转向的目标地址,这里是消息列表页面。

(4)authentication-failure-url,验证失败转向的页面,这里我们附加一个参数error,页面里面也有体现,就是用它来控制失败信息的打印的。

(5)logout-success-url就是退出后转向的页面,这里是到登录页面,没错,退出后回到登录页面。

二、从数据库中读取用户信息

三种方式:标准数据表方式、自定义数据表方式和属性文件properties方式

1.标准数据表

(1)数据表:

(2)CREATETABLE`users`(

(3)`USERNAME`varchar(10)NOTNULL,

(4)`PASSWORD`varchar(32)NOTNULL,

(5)`ENABLED`tinyint(1)NOTNULL,

(6)PRIMARYKEY(`USERNAME`)

(7))ENGINE=InnoDBDEFAULTCHARSET=utf8

(8)

(9)CREATETABLE`authorities`(

(10)`USERNAME`varchar(10)NOTNULL,

(11)`AUTHORITY`varchar(10)NOTNULL,

(12)KEY`FK_USERNAME_AUTHORITY`(`USERNAME`),

(13)CONSTRAINT`FK_USERNAME_AUTHORITY`FOREIGNKEY(`USERNAME`)REFERENCES`users`(`USERNAME`)ONDELETENOACTIONONUPDATENOACTION

(14))ENGINE=InnoDBDEFAULTCHARSET=utf8

用户与角色是一对多的关系,一个用户可以设置多个角色

(2)配置方式–认证管理器配置方式

1.<security:authentication-manager>

2.<security:authentication-provider>

3.<security:password-encoderref="md5Encoder"/>

4.<security:jdbc-user-servicedata-source-ref="dataSource"/>

5.</security:authentication-provider>

6.</security:authentication-manager>

这里有自定义的MD5加密器

<beanid="md5Encoder"class="org.ourpioneer.board.util.MD5Encoder"/>

2.自定义数据表:

(1)数据表

(2)CREATETABLE`b_user`(

(3)`ID`int(11)NOTNULLAUTO_INCREMENT,

(4)`USERNAME`varchar(20)NOTNULL,

(5)`PASSWORD`varchar(32)NOTNULL,

(6)PRIMARYKEY(`ID`)

(7))ENGINE=InnoDBAUTO_INCREMENT=4DEFAULTCHARSET=utf8

(8)CREATETABLE`b_userrole`(

(9)`ID`int(11)NOTNULL,

(10)`USERID`int(11)NOTNULL,

(11)`ROLE`varchar(15)NOTNULL,

(12)PRIMARYKEY(`ID`),

(13)KEY`FK_USERID_USERROLE`(`USERID`),

(14)CONSTRAINT`FK_USERID_USERROLE`FOREIGNKEY(`USERID`)REFERENCES`b_user`(`ID`)ONDELETENOACTIONONUPDATENOACTION

(15))ENGINE=InnoDBDEFAULTCHARSET=utf8

(2)配置方式:

由于表名,字段名和结构都和Security框架默认的不匹配,只好通过SQL语句来让Security框架识别了:

1.<security:authentication-manager>

2.<security:authentication-provider>

3.<security:password-encoderref="md5Encoder"/>

4.<security:jdbc-user-service

5.data-source-ref="dataSource"

6.users-by-username-query="selectUSERNAME,PASSWORD,'true'asENABLEDfromb_userwhereUSERNAME=?"

7.authorities-by-username-query="selectu.USERNAME,ur.ROLEasAUTHORITIESfromb_useru,b_userroleurwhereu.USERNAME=?andu.ID=ur.USERID"/>

8.</security:authentication-provider>

9.</security:authentication-manager>

注:user实际上是单个字段,用户名username,密码password,用户是否禁用(true/false)。此处true/false没有从数据库中提取,直接写为ENABLE,(DISABLE表示false)。

3.基于proptetis属性文件的配置方式

(1)属性文件格式:/WEB-INF/user.properties

1.admin=123,ROLE_ADMIN,ROLE_USER

2.user1=123,ROLE_USER

3.user2=123,enabled,ROLE_USER

这里面名/值对的形式排列的,值的字段比较多。admin/user1是用户名,不用多说,等号后面第一位是密码,这里没有加密。第二位是是否禁用状态,这是可选的,默认是enabled,第三位以后就是用户所拥有的角色了,这么使用和前面的效果也是相同的。

(2)配置方式—验证器

1.<security:authentication-manager>

2.<security:authentication-provider>

3.<security:user-serviceproperties="/WEB-INF/users.properties"/>

4.</security:authentication-provider>

5.</security:authentication-manager>

三、访问控制

访问控制是Security框架的另一大特性,可以对其进行自定义的扩展,设计符合我们业务逻辑的控制。这比URL拦截又深入了一步,可以过滤的东西又多了。

设计到访问控制,要引入一个概念,谁来决定能否访问,从而进行控制。Security框架中的访问控制管理有三种方案:至少有一个同意访问全部同意访问全部弃权或都同意访问(也就是没有拒绝的)。如何同意?投票产生!Security框架一个可配置的元素就出来了,那就是投票器了。和现实的投票一样,分同意,反对和弃权三类。

下面我们应用第一类访问控制管理:至少有一个投票器同意访问,在配置文件中这么来设置:

1.<beanid="accessDecisionManager"

2.class="org.springframework.security.access.vote.AffirmativeBased">

3.<propertyname="decisionVoters">

4.<list>

5.<beanclass="org.springframework.security.access.vote.RoleVoter"/>

6.<beanclass="org.springframework.security.access.vote.AuthenticatedVoter"/>

7.</list>

8.</property>

9.</bean>

先看看对控制器方法的安全访问,这个配置相对简单,在配置文件中,把安全配置文件和controller的声明放在一起:

1.<context:component-scanbase-package="org.ourpioneer.board.web"/>

2.<security:global-method-security

3.jsr250-annotations="enabled"secured-annotations="enabled"/>

四、SpringSecurity与CAS集成

http://www.family168.com/tutorial/springsecurity3/html/cas.html

示例文件:下载

http://www.jarvana.com/jarvana/browse/org/springframework/security/

1.所需jar文件

CasClient的jar包:

cas-client-core-3.1.10.jar

SpringSecurity的jar文件

spring-security-cas-client-3.0.5.RELEASE.jar

spring-security-config-3.0.5.RELEASE.jar

spring-security-core-3.0.5.RELEASE.jar

spring-security-web-3.0.5.RELEASE.jar

2.配置文件

(1)配置userService读取本地应用程序的角色权限信息

A.基于xml方式配置

user-service用户的信息配置,本应用程序自身要有一个用户列表,单点登录结束后需要从本地应用中读取角色信息下面是基于xml的配置方式,实际应用中应该使用数据库方式

<security:user-serviceid="userService">

<security:username="lgj"password="sailor"authorities="ROLE_SUPERVISOR,ROLE_USER"/>

<security:username="rod"password="rod"authorities="ROLE_SUPERVISOR,ROLE_USER"/>

<security:username="dianne"password="dianne"authorities="ROLE_USER"/>

<security:username="scott"password="scott"authorities="ROLE_USER"/>

</security:user-service>

B.基于数据库方式配置

<security:jdbc-user-serviceid="userService"

data-source-ref="dataSource"

users-by-username-query="selectUSERNAME,PASSWORD,'true'asENABLEDfromb_userwhereUSERNAME=?"

authorities-by-username-query="selectu.USERNAME,ur.ROLEasAUTHORITIESfromb_useru,b_userroleurwhereu.USERNAME=?andu.ID=ur.USERID"/>

(2)完整xml文件

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans"

xmlns:security="http://www.springframework.org/schema/security"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security-3.0.xsd">

<!--配置controller采用注解方式-->

<security:global-method-security

jsr250-annotations="enabled"secured-annotations="enabled"

access-decision-manager-ref="accessDecisionManager"/>

<!—配置退出url,拦截器等信息,由于上方使用了注解方式,因此拦截器可省略-->

<security:httpentry-point-ref="casProcessingFilterEntryPoint">

<security:logoutlogout-success-url="/cas-logout.jsp"/>

<security:custom-filterref="casAuthenticationFilter"after="CAS_FILTER"/>

</security:http>

<security:authentication-manageralias="authenticationManager">

<security:authentication-providerref="casAuthenticationProvider"/>

</security:authentication-manager>

<beanid="casAuthenticationFilter"class="org.springframework.security.cas.web.CasAuthenticationFilter">

<propertyname="authenticationManager"ref="authenticationManager"/>

<propertyname="authenticationFailureHandler">

<beanclass="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">

<propertyname="defaultFailureUrl"value="/casfailed.jsp"/>

</bean>

</property>

<propertyname="authenticationSuccessHandler">

<beanclass="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">

<propertyname="defaultTargetUrl"value="/"/>

</bean>

</property>

<propertyname="proxyGrantingTicketStorage"ref="proxyGrantingTicketStorage"/>

<propertyname="proxyReceptorUrl"value="/secure/receptor"/>

</bean>

<beanid="casProcessingFilterEntryPoint"class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">

<!--配置单点登录的地址-->

<propertyname="loginUrl"value="https://sso.qau.edu.cn/cas/login"/>

<propertyname="serviceProperties"ref="serviceProperties"/>

</bean>

<beanid="casAuthenticationProvider"class="org.springframework.security.cas.authentication.CasAuthenticationProvider">

<propertyname="userDetailsService"ref="userService"/>

<propertyname="serviceProperties"ref="serviceProperties"/>

<propertyname="ticketValidator">

<beanclass="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">

<constructor-argindex="0"value="https://sso.qau.edu.cn/cas"/>

<propertyname="proxyGrantingTicketStorage"ref="proxyGrantingTicketStorage"/>

<!--配置本地应用的地址-->

<propertyname="proxyCallbackUrl"value="http://210.44.48.15:8080/SpringMVCSpringJPA2CAS/secure/receptor"/>

</bean>

</property>

<propertyname="key"value="an_id_for_this_auth_provider_only"/>

</bean>

<beanid="proxyGrantingTicketStorage"class="org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl"/>

<beanid="serviceProperties"class="org.springframework.security.cas.ServiceProperties">

<propertyname="service"value="http://210.44.48.15:8080/SpringMVCSpringJPA2CAS/j_spring_cas_security_check"/>

<propertyname="sendRenew"value="false"/>

</bean>

<!--user-service用户的信息配置,本应用程序自身要有一个用户列表,单点登录结束后需要从本地应用中读取角色信息

下面是基于xml的配置方式,实际应用中应该使用数据库方式

<security:user-serviceid="userService">

<security:username="lgj"password="sailor"authorities="ROLE_SUPERVISOR,ROLE_USER"/>

<security:username="rod"password="rod"authorities="ROLE_SUPERVISOR,ROLE_USER"/>

<security:username="dianne"password="dianne"authorities="ROLE_USER"/>

<security:username="scott"password="scott"authorities="ROLE_USER"/>

</security:user-service>-->

<security:jdbc-user-serviceid="userService"

data-source-ref="dataSource"

users-by-username-query="selectUSERNAME,PASSWORD,'true'asENABLEDfromb_userwhereUSERNAME=?"

authorities-by-username-query="selectu.USERNAME,ur.ROLEasAUTHORITIESfromb_useru,b_userroleurwhereu.USERNAME=?andu.ID=ur.USERID"/>

<!--访问控制,可以进行自定义的扩展-->

<beanid="accessDecisionManager"

class="org.springframework.security.access.vote.AffirmativeBased">

<propertyname="decisionVoters">

<list>

<beanclass="org.springframework.security.access.vote.RoleVoter"/>

<beanclass="org.springframework.security.access.vote.AuthenticatedVoter"/>

<beanclass="cn.edu.qau.acl.IPAddressVoter"/>

</list>

</property>

</bean>

</beans>

(3)web.xml配置退出系统的过滤器和侦听器

<!--单点登出过滤器,应放到最上方-->

<filter>

<filter-name>CASSingleSignOutFilter</filter-name>

<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>CASSingleSignOutFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

<listener>

<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>

</listener>

(4)配置单点登录服务器的应用管理

3.访问方法

(1)获取principal对象

<p>Yourprincipalobjectis....:<%=request.getUserPrincipal()%></p>

(2)显示用户名

Welcome!

<security:authenticationproperty="name"/>--显示用户名

</h2>

角色:<security:authenticationproperty="authorities"var="authorities"/>

<ul>

<c:forEachitems="${authorities}"var="authority">

<li>

${authority.authority}

</li>

</c:forEach>

</ul>

或者:

<%

if(request.getRemoteUser()!=null){

out.println("<divclass=\"authenticated\">");

out.println("User:"+request.getRemoteUser());

//Logoutnotyetimplemented!

//out.println("  ");

//out.println("<ahref=\"#\">LOGOUT</a>");

out.println("</div>");

}else{

out.println("Notloggedin.");

}

%>

(3)角色判断

<%if(request.isUserInRole("ROLE_SUPERVISOR")){%>

Youareasupervisor!Youcanthereforeseethe<ahref="extreme/index.jsp">extremelysecurepage</a>.<br><br>

<%}%>

(4)显示错误信息

<fontcolor="red">

YourCAScredentialswererejected.<br/><br/>

Reason:<%=((AuthenticationException)session.getAttribute(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY)).getMessage()%>

</font>

(5)退出登录

退出session登录,此时还没有注销cas,重新刷新页面还可以拿到cas认证信息,并且登录成功:

<ahref="<c:urlvalue="/j_spring_security_logout"/>">LogoutSystem</a>

完全退出系统,并推出cas:

<p><ahref="https://sso.qau.edu.cn/cas/logout">LogoutofCAS</a></p>

(6)方法中调用认证信息

-------------------------------------------------------------以下来自------------------------------------------

http://www.family168.com/tutorial/springsecurity3/html/cas.html

security的中文翻译文档

Chapter21.CAS认证

21.1.概述

JA-SIG开发了一个企业级的单点登录系统,叫做CAS。与其他项目不同,JA-SIG的中心认证服务是开源的,广泛使用的,简单理解的,不依赖平台的,而且支持代理能力。SpringSecurity完全支持CAS,提供一个简单的整合方式,把使用SpringSecurity的单应用发布,转换成使用企业级CAS服务器的多应用发布安全

你可以从
http://www.ja-sig.org/cas/
找到CAS的更多信息。你还需要访问这个网站下载CAS服务器文件。

21.2.CAS是如何工作的

虽然CAS网站包含了CAS的架构文档,我们这里还是说一下使用SpringSecurity环境的一般性概述,。SpringSecurity3.0支持CAS3。在写文档的时候,CAS服务器的版本是3.3。

你要在公司内部安装CAS服务器。CAS服务器就是一个WAR文件,所以安装服务器没有什么难的。在WAR文件里,你需要自定义登录和其他单点登录展示给用户的页面。

发布CAS3.3的时候,你也需要指定一个CAS的
deployerConfigContext.xml
里包含的
AuthenticationHandler
AuthenticationHandler
有一个简单的方法,返回布尔值,判断给出的证书集合是否有效。你的
AuthenticationHandler
实现会需要链接到后台认证资源类型里,像是LDAP服务器或数据库。CAS自己也包含非常多
AuthenticationHandler
帮助实现这些。在你下载发布服务器war文件的时候,它会把用户名和密码匹配的用户成功验证,这对测试很有用。

除了CAS服务器,其他关键角色当然是你企业发布的其他安全web应用。这些web应用被叫做"services"。这儿有两种服务:标准服务和代理服务。代理服务可以代表用户,从其他服务中请求资源。后面会进行更详细的介绍。

21.3.配置CAS客户端

CAS的web应用端通过SpringSecurity使用起来很简单。我们假设你已经知道SpringSecurity的基本用法,所以下面都没有涉及这些。我们会假设使用基于命名空间配置的方法,并且添加了CAS需要的bean。

你需要添加一个
ServiceProperties
bean,到你的applicationcontext里。这表现你的CAS服务:

<beanid="serviceProperties"

class="org.springframework.security.cas.ServiceProperties">

<propertyname="service"

value="https://localhost:8443/cas-sample/j_spring_cas_security_check"/>

<propertyname="sendRenew"value="false"/>

</bean>



这里的
service
必须是一个由
CasAuthenticationFilter
监控的URL。这个
sendRenew
默认是false,但如果你的程序特别敏感就应该设置成true。这个参数作用是,告诉CAS登录服务,一个单点登录没有到达。否则,用户需要重新输入他们的用户名和密码,来获得访问服务的权限。

下面配置的bean就是展开CAS认证的过程(假设你使用了命名空间配置):

<security:httpentry-point-ref="casEntryPoint">

...

<custom-filterposition="FORM_LOGIN_FILTER"ref="myFilter"/>

</security:http>


<beanid="casFilter"

class="org.springframework.security.cas.web.CasAuthenticationFilter">

<propertyname="authenticationManager"ref="authenticationManager"/>

</bean>


<beanid="casEntryPoint"

class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">

<propertyname="loginUrl"value="https://localhost:9443/cas/login"/>

<propertyname="serviceProperties"ref="serviceProperties"/>

</bean>




应该使用
entry-point-ref
选择驱动认证的
CasAuthenticationEntryPoint
类。

CasAuthenticationFilter
的属性与
UsernamePasswordAuthenticationFilter
非常相似(在基于表单登录时用到)。

为了CAS的操作,
ExceptionTranslationFilter
必须有它的
AuthenticationEntryPoint
,这里设置成
CasAuthenticationEntryPoint
bean。

CasAuthenticationEntryPoint
必须指向
ServiceProperties
bean(上面讨论过了),它为企业CAS登录服务器提供URL。这是用户浏览器将被重定向的位置。

下一步,你需要添加一个
CasAuthenticationProvider
和它的合作伙伴:

<security:authentication-manageralias="authenticationManager">

<security:authentication-providerref="casAuthenticationProvider"/>

</security:authentication-manager>


<beanid="casAuthenticationProvider"

class="org.springframework.security.cas.authentication.CasAuthenticationProvider">

<propertyname="userDetailsService"ref="userService"/>

<propertyname="serviceProperties"ref="serviceProperties"/>

<propertyname="ticketValidator">

<beanclass="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">

<constructor-argindex="0"value="https://localhost:9443/cas"/>

</bean>

</property>

<propertyname="key"value="an_id_for_this_auth_provider_only"/>

</bean>


<security:user-serviceid="userService">

<security:username="joe"password="joe"authorities="ROLE_USER"/>

...

</security:user-service>



一旦通过了CAS的认证,CasAuthenticationProvider
使用一个
UserDetailsService
实例,来加载用户的认证信息。这里我们展示一个简单的基于内存的设置。

如果你翻回头看一下"HowCASWorks"那节,这些beans都是从名字就可以看懂的。

五、角色继承(待整理)

在SpringSecurity中,我们可以指定角色间的继承关系,这样可以重用角色权限,减少配置的代码量,让权限配置整体上显得更清晰。

为了使用角色继承功能,我们需要对原有的配置文件进行一些修改。

<authentication-provideruser-service-ref="userDetailsServiceWrapper"/>


<beans:beanid="userDetailsServiceWrapper"

class="org.springframework.security.userdetails.hierarchicalroles.UserDetailsServiceWrapper">

<beans:propertyname="userDetailsService"ref="userDetailsService"/>

<beans:propertyname="roleHierarchy">

<beans:beanclass="org.springframework.security.userdetails.hierarchicalroles.RoleHierarchyImpl">

<beans:propertyname="hierarchy"value="ROLE_ADMIN>ROLE_USER"/>

</beans:bean>

</beans:property>

</beans:bean>


<user-serviceid="userDetailsService">

<username="admin"password="admin"authorities="ROLE_ADMIN"/>

<username="user"password="user"authorities="ROLE_USER"/>

</user-service>



我们将原有的user-service单独抽离出来,在userDetailsService的基础上生成一个userDetailsServiceWrapper,这个wrapper的作用就是在原有的user-service的基础上启用角色继承功能。

我们使用RoleHierarchyImpl为userDetailsServiceWrapper配置了角色继承的策略,ROLE_ADMIN>ROLE_USER表示ROLE_ADMIN将继承ROLE_USER所有用的所有角色,只要是允许ROLE_USER访问的资源,ROLE_ADMIN也都有权限进行访问。这样我们在user-service中的配置就可以从ROLE_ADMIN,ROLE_USER简化为ROLE_ADMIN了,而intercept-url中的配置也可以从ROLE_ADMIN,ROLE_USER改为ROLE_USER了。

如果希望配置更多继承关系,可以使用换行进行分隔,比如:

<propertyname="hierarchy">

<value>

ROLE_A>ROLE_B

ROLE_B>ROLE_AUTHENTICATED

ROLE_AUTHENTICATED>ROLE_UNAUTHENTICATED

</value>

</property>



第51章角色继承

有两种方式可以用来实现角色继承,一种是使用UserDetailsServiceWrapper在读取用户信息时,使用角色继承为用户赋予全部的权限,另一种是使用RoleHierarchyVoter在判断权限的时候,使用角色继承为生成继承的全部权限。

使用UserDetailsServiceWrapper的情况,加载用户信息的同时用户会获得所有权限,所以使用Acl的情况也不会出问题。使用RoleHierarchyVoter的情况,因为只会在每次校验权限时才会使用继承获得所有权限,所以使用Acl的时候就会出现缺少权限的情况。

51.1.使用RoleHierarchyVoter

把RoleHierarchyVoter加入配置文件中的accessDecisionManager。

<beanid="accessDecisionManager"class="org.springframework.security.vote.AffirmativeBased">

<propertyname="allowIfAllAbstainDecisions"value="false"/>

<propertyname="decisionVoters">

<list>

<refbean="roleHierarchyVoter"/>

<refbean="authenticatedVoter"/>

</list>

</property>

</bean>

<beanid="roleHierarchyVoter"class="org.springframework.security.vote.RoleHierarchyVoter">

<constructor-argref="roleHierarchy"/>

</bean>

<beanid="authenticatedVoter"class="org.springframework.security.vote.AuthenticatedVoter"/>

<beanid="roleHierarchy"class="org.springframework.security.userdetails.hierarchicalroles.RoleHierarchyImpl">

<propertyname="hierarchy"value="ROLE_ADMIN>ROLE_USER"/>

</bean>

然后在http中引用这个accessDecisionManager即可。

<httpauto-config='true'access-decision-manager-ref='accessDecisionManager'>

<intercept-urlpattern="/admin.jsp"access="ROLE_ADMIN"/>

<intercept-urlpattern="/**"access="ROLE_USER"/>

</http>

51.2.使用数据库实现RoleHierarchy

实现代码来自http://jira.springframework.org/browse/SEC-1071。源代码在http://forum.springsource.org/showthread.php?t=65515。

使用基于Dao的RoleHierarchy需要两部设置。

第一是在数据库中建立对应的表结构,并设置初始数据,数据库表结构如下所示:

CREATEMEMORYTABLEROLE_HIERARCHIES(

INCLUDINGROLEVARCHAR(50),

INCLUDEDROLEVARCHAR(50)

);

includingrole表示继承了其他角色的角色。includedrole表示被继承的角色。如果我们希望设置ROLE_ADMIN继承ROLE_USER,只需要进行如下设置。

INSERTINTOROLE_HIERARCHIES(INCLUDINGROLE,INCLUDEDROLE)VALUES('ROLE_ADMIN','ROLE_USER')

这样RoleHierarchyEntryDaoJdbc会读取数据库中的数据,将其组装成ROLE_ADMIN>ROLE_USER格式,再交给RoleHierarchyImpl进行分析,最终用于RoleHierarchyVoter完成角色继承的判断。

下面修改配置文件,将daoRoleHierarchy的bean设置到配置文件中。

<beanid="roleHierarchyVoter"class="org.springframework.security.vote.RoleHierarchyVoter">

<constructor-argref="daoRoleHierarchy"/>

</bean>

<beanid="daoRoleHierarchy"

class="org.springframework.security.userdetails.hierarchicalroles.RoleHierarchyImplDaoAware">

<propertyname="_roleHierarchyEntryDao"ref="roleHierarchyEntryDaoJdbc"/>

</bean>

<beanid="roleHierarchyEntryDaoJdbc"

class="org.springframework.security.userdetails.hierarchicalroles.RoleHierarchyEntryDaoJdbc">

<propertyname="dataSource"ref="dataSource"/>

</bean>

这样就完成了使用数据库保存角色继承信息的功能。

六、角色分组功能(待整理)

使用用户组

SpringSecurity提供了用户组机制,可以将多个用户归纳在一个组中,进行统一授权。下面我们来研究一下如何在数据库中使用用户组保存用户的权限信息。

44.1.数据库结构

在原数据库的基础上添加用户组所需的三张表:

createtablegroups(

idbigintgeneratedbydefaultasidentity(startwith0)primarykey,

group_namevarchar_ignorecase(50)notnull

);


createtablegroup_authorities(

group_idbigintnotnull,

authorityvarchar(50)notnull,

constraintfk_group_authorities_groupforeignkey(group_id)referencesgroups(id)

);


createtablegroup_members(

idbigintgeneratedbydefaultasidentity(startwith0)primarykey,

usernamevarchar(50)notnull,

group_idbigintnotnull,

constraintfk_group_members_groupforeignkey(group_id)referencesgroups(id)

);



ER图如下所示:

44.1.用户组ER

下面开始初始化数据:

insertintousers(username,password,enabled)values('admin','admin',true);

insertintousers(username,password,enabled)values('user','user',true);


insertintogroups(id,group_name)values(1,'admin');

insertintogroups(id,group_name)values(2,'user');


insertintogroup_authorities(group_id,authority)values(1,'ROLE_ADMIN');

insertintogroup_authorities(group_id,authority)values(2,'ROLE_USER');


insertintogroup_members(id,username,group_id)values(1,'admin',1);

insertintogroup_members(id,username,group_id)values(2,'admin',2);

insertintogroup_members(id,username,group_id)values(3,'user',2);



创建两个用户admin和user。

创建两个组admin和user。

为用户组授权,让admin组中的所有用户都拥有ROLE_ADMIN权限,user组中的所有用户都拥有ROLE_USER权限。

admin用户加入admin和user两个用户组,将user用户加入user用户组。

44.2.修改配置文件

为jdbc-user-service添加一个参数就可以打开用户组功能。

<authentication-provider>

<jdbc-user-servicedata-source-ref="dataSource"

group-authorities-by-username-query="

SELECTg.id,g.group_name,ga.authority

FROMgroupsg,group_membersgm,group_authoritiesga

WHEREgm.username=?

ANDg.id=ga.group_id

ANDg.id=gm.group_id"/>

</authentication-provider>



这样系统就会使用这条sql语句从用户组表中查询用户拥有的权限。

之后可以启动实例ch212,使用admin和user用户测试授权情况。

七、ACL访问控制列表(不同的人只能看到自己的信息)

http://www.family168.com/oa/springsecurity/html/ch301-acl.html

八、RESTful风格权限控制

RESTful是蛮流行的一种web资源访问方式,它最大限度利用了http协议的功能表达资源请求的内涵。

GET/app/messages:表示获得所有Message的信息。

GET/app/messages/1:表示获得id=1的Message的信息。

POST/app/messages:表示创建一条Message信息。

PUT/app/messages/1:表示更新id=1的Message的信息。

DELETE/app/messages/1:表示删除id=1的Message的信息。

从上面的例子中可以看出来,我们只用了/app/messages和/app/messages/*两类URL,就表示了Message的CRUD所有操作。这时如果我们希望对这些操作进行权限控制,就不能仅仅根据URL地址来判断了,而是需要限制http请求的method参数。

下面我们对这些操作做出限制,只有ROLE_ADMIN才能添加,更新,删除Message,ROLE_USER只能查看Message的信息。

<httpauto-config='true'>

<intercept-urlpattern="/admin.jsp"access="ROLE_ADMIN"/>

<intercept-urlpattern="/app/messages"access="ROLE_ADMIN"method="POST"/>

<intercept-urlpattern="/app/messages/*"access="ROLE_ADMIN"method="PUT"/>

<intercept-urlpattern="/app/messages/*"access="ROLE_ADMIN"method="DELETE"/>

<intercept-urlpattern="/**"access="ROLE_USER"/>

</http>



在intercept-url中使用method指定对应的http请求method参数就可以限制REST下定义的不同功能请求了,可以每次只能定义一个method,如果支持method="PUT,DELETE"这种形式的配置就更方便了。

实例参考x05。

【七】开发配置规范:

1.配置文件命名与位置

2.数据库名

3.数据表名

4.类名

5.urls规划

/模块名/程序名/系列参数

6.文件名
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: