【一】Spring应用
Spring 支持json格式的jar
jackson-all-1.7.3 http://jackson.codehaus.org/
Spring MVC 3.x annotated controller的几点心得体会
(最优化使用http://www.javaeye.com/topic/828513)
一、callback回调
template method(模板方法设计模式)-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使用
Spring MVC核心 流程示意图
Dispatcher Servlet --front controller前端总控制器
Controller --应用开发的控制器,实现controller接口,
Handler Mapping --控制器的映射,控制controller策略
ViewResolver & View --视图策略,View是试图预处理
InternalResourceViewResolver --jsp和jstl的解析器,
Interceptors :拦截器
LocalResolver :国际化
Validate :验证
ClassNameHandlerMapping 类名控制器--按类名匹配的abc.test的AbcController
BeanNameUrlHandlerMapping <bean name="/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}")
public String start(@PathVariable("name") String name,@PathVariable("age") int
nianling){ //此处name与上面的name一致,
}
//此处注意:java编辑的debug模式 和release模式
三、根据不同的请求方法(POST/GET),映射到不同的处理方法
@RequestMapping(value="/start",method=RequestMethod.GET)
public String PostStart(){ /
}
@RequestMapping(value="/start",method=RequestMethod.POST)
public String start(){ /
}
四、数据绑定,注解中的实现
@RequestMapping(value="/{test}",method=RequestMethod.GET)
public String start(){ /
}
@Initbinder --初始化绑定器标注
public void initbinder(WebRequestDataBinder binder,HttpServletRequest request){
binder.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat
("yyyy-MM-dd"),false));
}
五、处理请求的方法,接收的参数和返回的参数
1.接收的参数:数量与顺序没有要求
=
(1)没有参数()
(2)(@Pathvariable anyType)
(3)(request,response,session,)
//session的前提,当前请求session可用。
(4)request参数@RequestParam anyType //--?id=45参数,通过类型转换器转换
(5)@CookieValue anyType CookieName //cookie值取出,通过类型转换
(6)@RequestHeader("user-Agent") AnyType abc --从请求的头部中取出正确的值,例如(user-
Agent,content etc)
(7)经常调用out = response.getWriter()
@RequestMapping
public void testAllArguments(PrintWriter out){
//out = response.getWriter();
- out.println();
}
(8) 可以是Map
public ModelAndView test(Map model){
//返回modelandview
model.put("","");
return new ModelAndView("viewName");
}
(9)表单参数生成bean 注入
public String test(User user,BindingResult result)
//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自动填充到模型中。试图名为默认解析
的名字
public User testUser(){
}
(4)返回list --//model.("userList",list)
public List<User> queryUsers(){
return null;
}
(5)Model,Map,ModelAndView 只返回模型是,试图名按默认解析
六、重定向视图 return "redirect:/usr_list"
@Controller
@RequestMammping("/user")
publc class UserController{
@RequestMapping(method=RequestMethod.GET)
public String addForm(){
return "add_user";
}
@RequestMapping(method=RequestMethod.POST)
public String addUser(User user){
return "redirect:/user_list"; //重定向视图,防止再次进行提交操作,重复提交
}
}
产生原因:request.forward();-带着参数,由服务器转到另外一个方法
response.sendRedirect(b) --重定向方法,不带参数转换,浏览器知道发生了改变,浏览器 再次
发生一次请求b
解决方法:将结果重定向到 另一个页面:return "redirect:success";
七、ExcelView JsperReport,Pdf,XML视图
八、拦截器,视图的国际化,表单验证
1.拦截器:全局日志,权限管理(拦截器细粒度)
(1)定义一个拦截器,多个拦截器形成链:实现HanderInerceptor接口,三个方法
preHandler()
postHandler()
afterCompletion()
(2)配置拦截器:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/usr/*"/> --可选
<bean class="test.web.myInerceptor"/>
</mvc:interceptor>
</mvc:interceptors>
2.国际化(视图)--中文/英文/日文/韩文
引入spring标签库:
<%@ tablib uri="http://www.springframework.org/tags" prefix="spring" %>
<div>
<spring:message code="welcome"/> --引用属性文件的welcome
</div>
配置资源文件
<bean id="resourceMessage(此名不可变)"
class="org.sf.context.support.resourceBundleMessageSource">
<property name="basename" value="mess"/>
<property name="basenames">
<value>...</value>
<value>...</value>
</property>
</bean>
3.验证Validate(Spring后端验证采用JSR-303 validate bean标准,前断用javascript)
后端用@Valid注解 --自动调用约束条件验证,条件在实体类中指定
比较的好的实现:hibernate - validator 4.0
class User
{
@NotEmpty
private string name
@Size(max=20,min=6)
private String password;
}
public String addUser(@Valid User user,BindingResult result){
}
九、对json的原生支持
spring mvc ajax 使用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. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="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. http://www.springframework.org/schema/security/spring-security-3.0.xsd">
12. <context:component-scan base-package="org.ourpioneer.board.web" />
13. <bean
14. class="org.springframework.web.servlet.view.InternalResourceViewResolver">
15. <property name="prefix" value="/WEB-INF/jsp/" />
16. <property name="suffix" value=".jsp" />
17. </bean>
18. <security:global-method-security
19. jsr250-annotations="enabled" secured-annotations="enabled" />
20. </beans>
以上配置springsecurity配置为支持注释形式。Spring security配置的文件,必须在springmvc初始化的配置文件中初始化,否则不起作用。也就是说secrrity的配置文件必须和spring mvc的配置文件 都被spring MVC放在一起(web.xml配置springmvc时,init-para也要把security的放进去)框架加载。
3.配置自定义登录界面
1. <security:http access-denied-page="/accessDenied.jsp" >
2. <security:form-login login-page="/login.jsp"
3. login-processing-url="/login" default-target-url="/messageList.htm"
4. authentication-failure-url="/login.jsp?error=true" />
5. <security:logout logout-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) CREATE TABLE `users` (
(3) `USERNAME` varchar(10) NOT NULL,
(4) `PASSWORD` varchar(32) NOT NULL,
(5) `ENABLED` tinyint(1) NOT NULL,
(6) PRIMARY KEY (`USERNAME`)
(7) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
(8)
(9) CREATE TABLE `authorities` (
(10) `USERNAME` varchar(10) NOT NULL,
(11) `AUTHORITY` varchar(10) NOT NULL,
(12) KEY `FK_USERNAME_AUTHORITY` (`USERNAME`),
(13) CONSTRAINT `FK_USERNAME_AUTHORITY` FOREIGN KEY (`USERNAME`) REFERENCES `users` (`USERNAME`) ON DELETE NO ACTION ON UPDATE NO ACTION
(14) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
用户与角色是一对多的关系,一个用户可以设置多个角色
(2)配置方式 –认证管理器配置方式
1. <security:authentication-manager>
2. <security:authentication-provider>
3. <security:password-encoder ref="md5Encoder" />
4. <security:jdbc-user-service data-source-ref="dataSource" />
5. </security:authentication-provider>
6. </security:authentication-manager>
这里有自定义的MD5加密器
<bean id="md5Encoder" class="org.ourpioneer.board.util.MD5Encoder" />
2.自定义数据表:
(1) 数据表
(2) CREATE TABLE `b_user` (
(3) `ID` int(11) NOT NULL AUTO_INCREMENT,
(4) `USERNAME` varchar(20) NOT NULL,
(5) `PASSWORD` varchar(32) NOT NULL,
(6) PRIMARY KEY (`ID`)
(7) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
(8) CREATE TABLE `b_userrole` (
(9) `ID` int(11) NOT NULL,
(10) `USERID` int(11) NOT NULL,
(11) `ROLE` varchar(15) NOT NULL,
(12) PRIMARY KEY (`ID`),
(13) KEY `FK_USERID_USERROLE` (`USERID`),
(14) CONSTRAINT `FK_USERID_USERROLE` FOREIGN KEY (`USERID`) REFERENCES `b_user` (`ID`) ON DELETE NO ACTION ON UPDATE NO ACTION
(15) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
(2)配置方式:
由于表名,字段名和结构都和Security框架默认的不匹配,只好通过SQL语句来让Security框架识别了:
1. <security:authentication-manager>
2. <security:authentication-provider>
3. <security:password-encoder ref="md5Encoder" />
4. <security:jdbc-user-service
5. data-source-ref="dataSource"
6. users-by-username-query="select USERNAME,PASSWORD,'true' as ENABLED from b_user where USERNAME=?"
7. authorities-by-username-query="select u.USERNAME,ur.ROLE as AUTHORITIES from b_user u,b_userrole ur where u.USERNAME=? and u.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-service properties="/WEB-INF/users.properties"/>
4. </security:authentication-provider>
5. </security:authentication-manager>
三、访问控制
访问控制是Security框架的另一大特性,可以对其进行自定义的扩展,设计符合我们业务逻辑的控制。这比URL拦截又深入了一步,可以过滤的东西又多了。
设计到访问控制,要引入一个概念,谁来决定能否访问,从而进行控制。Security框架中的访问控制管理有三种方案:至少有一个同意访问,全部同意访问,全部弃权或都同意访问(也就是没有拒绝的)。如何同意?投票产生!Security框架一个可配置的元素就出来了,那就是投票器了。和现实的投票一样,分同意,反对和弃权三类。
下面我们应用第一类访问控制管理:至少有一个投票器同意访问,在配置文件中这么来设置:
1. <bean id="accessDecisionManager"
2. class="org.springframework.security.access.vote.AffirmativeBased">
3. <property name="decisionVoters">
4. <list>
5. <bean class="org.springframework.security.access.vote.RoleVoter" />
6. <bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
7. </list>
8. </property>
9. </bean>
先看看对控制器方法的安全访问,这个配置相对简单,在配置文件中,把安全配置文件和controller的声明放在一起:
1. <context:component-scan base-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文件
Cas Client的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-service id="userService">
<security:user name="lgj" password="sailor" authorities="ROLE_SUPERVISOR,ROLE_USER" />
<security:user name="rod" password="rod" authorities="ROLE_SUPERVISOR,ROLE_USER" />
<security:user name="dianne" password="dianne" authorities="ROLE_USER" />
<security:user name="scott" password="scott" authorities="ROLE_USER" />
</security:user-service>
B.基于数据库方式配置
<security:jdbc-user-service id="userService"
data-source-ref="dataSource"
users-by-username-query="select USERNAME,PASSWORD,'true' as ENABLED from b_user where USERNAME=?"
authorities-by-username-query="select u.USERNAME,ur.ROLE as AUTHORITIES from b_user u,b_userrole ur where u.USERNAME=? and u.ID=ur.USERID" />
(2)完整xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="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/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://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:http entry-point-ref="casProcessingFilterEntryPoint">
<security:logout logout-success-url="/cas-logout.jsp"/>
<security:custom-filter ref="casAuthenticationFilter" after="CAS_FILTER"/>
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthenticationProvider"/>
</security:authentication-manager>
<bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureHandler">
<bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/casfailed.jsp"/>
</bean>
</property>
<property name="authenticationSuccessHandler">
<bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/"/>
</bean>
</property>
<property name="proxyGrantingTicketStorage" ref="proxyGrantingTicketStorage" />
<property name="proxyReceptorUrl" value="/secure/receptor" />
</bean>
<bean id="casProcessingFilterEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<!-- 配置单点登录的地址 -->
<property name="loginUrl" value="https://sso.qau.edu.cn/cas/login"/>
<property name="serviceProperties" ref="serviceProperties"/>
</bean>
<bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<property name="userDetailsService" ref="userService"/>
<property name="serviceProperties" ref="serviceProperties" />
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<constructor-arg index="0" value="https://sso.qau.edu.cn/cas" />
<property name="proxyGrantingTicketStorage" ref="proxyGrantingTicketStorage" />
<!-- 配置本地应用的地址 -->
<property name="proxyCallbackUrl" value="http://210.44.48.15:8080/SpringMVCSpringJPA2CAS/secure/receptor" />
</bean>
</property>
<property name="key" value="an_id_for_this_auth_provider_only"/>
</bean>
<bean id="proxyGrantingTicketStorage" class="org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl" />
<bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<property name="service" value="http://210.44.48.15:8080/SpringMVCSpringJPA2CAS/j_spring_cas_security_check"/>
<property name="sendRenew" value="false"/>
</bean>
<!-- user-service 用户的信息配置,本应用程序自身要有一个用户列表,单点登录结束后需要从本地应用中读取角色信息
下面是基于xml的配置方式,实际应用中应该使用数据库方式
<security:user-service id="userService">
<security:user name="lgj" password="sailor" authorities="ROLE_SUPERVISOR,ROLE_USER" />
<security:user name="rod" password="rod" authorities="ROLE_SUPERVISOR,ROLE_USER" />
<security:user name="dianne" password="dianne" authorities="ROLE_USER" />
<security:user name="scott" password="scott" authorities="ROLE_USER" />
</security:user-service> -->
<security:jdbc-user-service id="userService"
data-source-ref="dataSource"
users-by-username-query="select USERNAME,PASSWORD,'true' as ENABLED from b_user where USERNAME=?"
authorities-by-username-query="select u.USERNAME,ur.ROLE as AUTHORITIES from b_user u,b_userrole ur where u.USERNAME=? and u.ID=ur.USERID" />
<!-- 访问控制,可以进行自定义的扩展 -->
<bean id="accessDecisionManager"
class="org.springframework.security.access.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<bean class="org.springframework.security.access.vote.RoleVoter" />
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
<bean class="cn.edu.qau.acl.IPAddressVoter" />
</list>
</property>
</bean>
</beans>
(3)web.xml配置退出系统的过滤器和侦听器
<!-- 单点登出过滤器,应放到最上方 -->
<filter>
<filter-name>CAS Single Sign Out Filter</filter-name>
<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS Single Sign Out Filter</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>Your principal object is....: <%= request.getUserPrincipal() %></p>
(2)显示用户名
Welcome!
<security:authentication property="name" /> --显示用户名
</h2>
角色:<security:authentication property="authorities" var="authorities" />
<ul>
<c:forEach items="${authorities}" var="authority">
<li>
${authority.authority}
</li>
</c:forEach>
</ul>
或者:
<%
if (request.getRemoteUser() != null) {
out.println("<div class=\"authenticated\">");
out.println("User: " + request.getRemoteUser());
//Logout not yet implemented!
//out.println(" ");
//out.println("<a href=\"#\">LOGOUT</a>");
out.println("</div>");
} else {
out.println("Not logged in.");
}
%>
(3)角色判断
<%if (request.isUserInRole("ROLE_SUPERVISOR")) { %>
You are a supervisor! You can therefore see the <a href="extreme/index.jsp">extremely secure page</a>.<br><br>
<% } %>
(4)显示错误信息
<font color="red">
Your CAS credentials were rejected.<br/><br/>
Reason: <%= ((AuthenticationException) session.getAttribute(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY)).getMessage() %>
</font>
(5)退出登录
退出session登录,此时还没有注销cas,重新刷新页面还可以拿到cas认证信息,并且登录成功:
<a href="<c:url value="/j_spring_security_logout" />">Logout System</a>
完全退出系统,并推出cas:
<p><a href="https://sso.qau.edu.cn/cas/logout">Logout of CAS</a></p>
(6)方法中调用认证信息
-------------------------------------------------------------以下来自------------------------------------------
http://www.family168.com/tutorial/springsecurity3/html/cas.html
security 的中文翻译文档
Chapter 21. CAS认证
21.1. 概述
JA-SIG开发了一个企业级的单点登录系统,叫做CAS。 与其他项目不同,JA-SIG的中心认证服务是开源的,广泛使用的,简单理解的,不依赖平台的,而且支持代理能力。 Spring Security完全支持CAS,提供一个简单的整合方式,把使用Spring Security的单应用发布,转换成使用企业级CAS服务器的多应用发布安全
你可以从http://www.ja-sig.org/cas/
找到CAS的更多信息。 你还需要访问这个网站下载CAS服务器文件。
21.2. CAS是如何工作的
虽然CAS网站包含了CAS的架构文档,我们这里还是说一下使用Spring Security环境的一般性概述,。 Spring Security 3.0支持CAS 3。 在写文档的时候,CAS服务器的版本是3.3。
你要在公司内部安装CAS服务器。 CAS服务器就是一个WAR文件,所以安装服务器没有什么难的。 在WAR文件里,你需要自定义登录和其他单点登录展示给用户的页面。
发布CAS 3.3的时候,你也需要指定一个CAS的deployerConfigContext.xml
里包含的AuthenticationHandler
。 AuthenticationHandler
有一个简单的方法,返回布尔值,判断给出的证书集合是否有效。 你的AuthenticationHandler
实现会需要链接到后台认证资源类型里,像是LDAP服务器或数据库。 CAS自己也包含非常多AuthenticationHandler
帮助实现这些。 在你下载发布服务器war文件的时候,它会把用户名和密码匹配的用户成功验证,这对测试很有用。
除了CAS服务器,其他关键角色当然是你企业发布的其他安全web应用。 这些web应用被叫做"services"。 这儿有两种服务:标准服务和代理服务。 代理服务可以代表用户,从其他服务中请求资源。 后面会进行更详细的介绍。
21.3. 配置CAS客户端
CAS的web应用端通过Spring Security使用起来很简单。 我们假设你已经知道Spring Security的基本用法,所以下面都没有涉及这些。 我们会假设使用基于命名空间配置的方法,并且添加了CAS需要的bean。
你需要添加一个 ServiceProperties
bean,到你的application context里。 这表现你的CAS服务:
<bean id="serviceProperties"
class="org.springframework.security.cas.ServiceProperties">
<property name="service"
value="https://localhost:8443/cas-sample/j_spring_cas_security_check"/>
<property name="sendRenew" value="false"/>
</bean>
这里的service
必须是一个由CasAuthenticationFilter
监控的URL。 这个sendRenew
默认是false,但如果你的程序特别敏感就应该设置成true。 这个参数作用是,告诉CAS登录服务,一个单点登录没有到达。 否则,用户需要重新输入他们的用户名和密码,来获得访问服务的权限。
下面配置的bean就是展开CAS认证的过程(假设你使用了命名空间配置):
<security:http entry-point-ref="casEntryPoint">
...
<custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" />
</security:http>
<bean id="casFilter"
class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="casEntryPoint"
class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<property name="loginUrl" value="https://localhost:9443/cas/login"/>
<property name="serviceProperties" ref="serviceProperties"/>
</bean>
应该使用 entry-point-ref
选择驱动认证的CasAuthenticationEntryPoint
类。
CasAuthenticationFilter
的属性与 UsernamePasswordAuthenticationFilter
非常相似(在基于表单登录时用到)。
为了CAS的操作,ExceptionTranslationFilter
必须有它的AuthenticationEntryPoint
,这里设置成CasAuthenticationEntryPoint
bean。
CasAuthenticationEntryPoint
必须指向 ServiceProperties
bean (上面讨论过了),它为企业CAS登录服务器提供URL。 这是用户浏览器将被重定向的位置。
下一步,你需要添加一个CasAuthenticationProvider
和它的合作伙伴:
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthenticationProvider" />
</security:authentication-manager>
<bean id="casAuthenticationProvider"
class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<property name="userDetailsService" ref="userService"/>
<property name="serviceProperties" ref="serviceProperties" />
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<constructor-arg index="0" value="https://localhost:9443/cas" />
</bean>
</property>
<property name="key" value="an_id_for_this_auth_provider_only"/>
</bean>
<security:user-service id="userService">
<security:user name="joe" password="joe" authorities="ROLE_USER" />
...
</security:user-service>
一旦通过了CAS的认证,CasAuthenticationProvider
使用一个 UserDetailsService
实例,来加载用户的认证信息。 这里我们展示一个简单的基于内存的设置。
如果你翻回头看一下"How CAS Works"那节,这些beans都是从名字就可以看懂的。
五、角色继承(待整理)
在Spring Security中,我们可以指定角色间的继承关系,这样可以重用角色权限,减少配置的代码量,让权限配置整体上显得更清晰。
为了使用角色继承功能,我们需要对原有的配置文件进行一些修改。
<authentication-provider user-service-ref="userDetailsServiceWrapper"/>
<beans:bean id="userDetailsServiceWrapper"
class="org.springframework.security.userdetails.hierarchicalroles.UserDetailsServiceWrapper">
<beans:property name="userDetailsService" ref="userDetailsService"/>
<beans:property name="roleHierarchy">
<beans:bean class="org.springframework.security.userdetails.hierarchicalroles.RoleHierarchyImpl">
<beans:property name="hierarchy" value="ROLE_ADMIN > ROLE_USER"/>
</beans:bean>
</beans:property>
</beans:bean>
<user-service id="userDetailsService">
<user name="admin" password="admin" authorities="ROLE_ADMIN" />
<user name="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了。
如果希望配置更多继承关系,可以使用换行进行分隔,比如:
<property name="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。
<bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<ref bean="roleHierarchyVoter"/>
<ref bean="authenticatedVoter"/>
</list>
</property>
</bean>
<bean id="roleHierarchyVoter" class="org.springframework.security.vote.RoleHierarchyVoter">
<constructor-arg ref="roleHierarchy"/>
</bean>
<bean id="authenticatedVoter" class="org.springframework.security.vote.AuthenticatedVoter"/>
<bean id="roleHierarchy" class="org.springframework.security.userdetails.hierarchicalroles.RoleHierarchyImpl">
<property name="hierarchy" value="ROLE_ADMIN > ROLE_USER"/>
</bean>
然后在http中引用这个accessDecisionManager即可。
<http auto-config='true' access-decision-manager-ref='accessDecisionManager'>
<intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />
<intercept-url pattern="/**" access="ROLE_USER" />
</http>
51.2. 使用数据库实现RoleHierarchy
实现代码来自http://jira.springframework.org/browse/SEC-1071。源代码在http://forum.springsource.org/showthread.php?t=65515。
使用基于Dao的RoleHierarchy需要两部设置。
第一是在数据库中建立对应的表结构,并设置初始数据,数据库表结构如下所示:
CREATE MEMORY TABLE ROLE_HIERARCHIES(
INCLUDINGROLE VARCHAR(50),
INCLUDEDROLE VARCHAR(50)
);
includingrole表示继承了其他角色的角色。includedrole表示被继承的角色。如果我们希望设置ROLE_ADMIN继承ROLE_USER,只需要进行如下设置。
INSERT INTO ROLE_HIERARCHIES(INCLUDINGROLE,INCLUDEDROLE) VALUES('ROLE_ADMIN','ROLE_USER')
这样RoleHierarchyEntryDaoJdbc会读取数据库中的数据,将其组装成ROLE_ADMIN > ROLE_USER格式,再交给RoleHierarchyImpl进行分析,最终用于RoleHierarchyVoter完成角色继承的判断。
下面修改配置文件,将daoRoleHierarchy的bean设置到配置文件中。
<bean id="roleHierarchyVoter" class="org.springframework.security.vote.RoleHierarchyVoter">
<constructor-arg ref="daoRoleHierarchy"/>
</bean>
<bean id="daoRoleHierarchy"
class="org.springframework.security.userdetails.hierarchicalroles.RoleHierarchyImplDaoAware">
<property name="_roleHierarchyEntryDao" ref="roleHierarchyEntryDaoJdbc"/>
</bean>
<bean id="roleHierarchyEntryDaoJdbc"
class="org.springframework.security.userdetails.hierarchicalroles.RoleHierarchyEntryDaoJdbc">
<property name="dataSource" ref="dataSource"/>
</bean>
这样就完成了使用数据库保存角色继承信息的功能。
六、角色分组功能(待整理)
使用用户组
Spring Security提供了用户组机制,可以将多个用户归纳在一个组中,进行统一授权。下面我们来研究一下如何在数据库中使用用户组保存用户的权限信息。
44.1. 数据库结构
在原数据库的基础上添加用户组所需的三张表:
create table groups (
id bigint generated by default as identity(start with 0) primary key,
group_name varchar_ignorecase(50) not null
);
create table group_authorities (
group_id bigint not null,
authority varchar(50) not null,
constraint fk_group_authorities_group foreign key(group_id) references groups(id)
);
create table group_members (
id bigint generated by default as identity(start with 0) primary key,
username varchar(50) not null,
group_id bigint not null,
constraint fk_group_members_group foreign key(group_id) references groups(id)
);
ER图如下所示:
图 44.1. 用户组ER图
下面开始初始化数据:
insert into users(username,password,enabled) values('admin','admin',true);
insert into users(username,password,enabled) values('user','user',true);
insert into groups(id,group_name) values(1,'admin');
insert into groups(id,group_name) values(2,'user');
insert into group_authorities(group_id,authority) values(1,'ROLE_ADMIN');
insert into group_authorities(group_id,authority) values(2,'ROLE_USER');
insert into group_members(id,username,group_id) values(1,'admin',1);
insert into group_members(id,username,group_id) values(2,'admin',2);
insert into group_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-service data-source-ref="dataSource"
group-authorities-by-username-query="
SELECT g.id, g.group_name, ga.authority
FROM groups g, group_members gm, group_authorities ga
WHERE gm.username = ?
AND g.id = ga.group_id
AND g.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的信息。
<http auto-config='true'>
<intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />
<intercept-url pattern="/app/messages" access="ROLE_ADMIN" method="POST" />
<intercept-url pattern="/app/messages/*" access="ROLE_ADMIN" method="PUT" />
<intercept-url pattern="/app/messages/*" access="ROLE_ADMIN" method="DELETE" />
<intercept-url pattern="/**" access="ROLE_USER" />
</http>
在intercept-url中使用method指定对应的http请求method参数就可以限制REST下定义的不同功能请求了,可以每次只能定义一个method,如果支持method="PUT,DELETE"这种形式的配置就更方便了。
实例参考x05。
【七】开发配置规范:
1.配置文件命名与位置
2.数据库名
3.数据表名
4.类名
5.urls规划
/模块名/程序名/系列参数
6.文件名