Spring下的权限框架 spring security总结

来源:互联网 发布:得力33068 软件下载 编辑:程序博客网 时间:2024/05/20 15:10
Spring下的权限框架 spring security总结
Java代码
  1. spring security总结
  2. 首先导入spring security所需要的jar包
  3. spring-security-core-2.0.5.RELEASE.jar
  4. spring-security-core-tiger-2.0.5.RELEASE.jar
  5. 一.配置过滤器
  6. 在web.xml中定义如下过滤器
  7. <filter>
  8. <filter-name>springSecurityFilterChain</filter-name>
  9. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  10. </filter>
  11. <filter-mapping>
  12. <filter-name>springSecurityFilterChain</filter-name>
  13. <url-pattern>
  14. CREATE TABLE `t_account` (
  15. `id` int(11)NOT NULL,
  16. `username` varchar(255)defaultNULL,
  17. `password` varchar(255)defaultNULL,
  18. `enabled` intdefaultNULL,
  19. PRIMARY KEY (`id`)
  20. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  21. CREATE TABLE `t_role` (
  22. `id` int(11)NOT NULL,
  23. `name` varchar(255)defaultNULL,
  24. `descn` varchar(255)defaultNULL,
  25. PRIMARY KEY (`id`)
  26. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  27. CREATE TABLE `t_account_role` (
  28. `a_id` int(11)NOT NULL,
  29. `r_id` int(11)NOT NULL,
  30. PRIMARY KEY (`a_id`,`r_id`),
  31. KEY `FK1C2BC9332D31C656` (`r_id`),
  32. KEY `FK1C2BC93371CCC630` (`a_id`),
  33. CONSTRAINT `FK1C2BC93384B0A30E` FOREIGN KEY (`a_id`) REFERENCES `t_account` (`id`),
  34. CONSTRAINT `FK1C2BC9332D31C656` FOREIGN KEY (`r_id`) REFERENCES `t_role` (`id`)
  35. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  36. insert into t_account values(1,'zhangsan','123',1);
  37. insert into t_account values(2,'lisi','321',1);
  38. insert into t_role values(1,'系统管理员','ROLE_ADMIN');
  39. insert into t_role values(2,'普通用户','ROLE_USER');
  40. insert into t_account_role values(1,2);
  41. insert into t_account_role values(2,1);
  42. 当用户登录时,spring security首先判断用户是否可以登录。用户登录后spring security获得该用户的
  43. 所有权限以判断用户是否可以访问资源。
  44. spring配置文件中定义
  45. <authentication-provider>
  46. <jdbc-user-service data-source-ref="dataSource"
  47. users-by-username-query="selectusername,password,enabled from t_account where username=?"
  48. authorities-by-username-query="selectr.descn from t_account_role ar join
  49. t_account a on ar.a_id=a.id join t_role r on ar.r_id=r.id where a.username=?"/>
  50. </authentication-provider>
  51. users-by-username-query:根据用户名查找用户
  52. authorities-by-username-query:根据用户名查找这个用户所有的角色名,将用户访问的URL地址和
  53. 查询结果与<intercept-url pattern="/admin.jsp"access="ROLE_ADMIN"/>标签进行匹配。
  54. 匹配成功就允许访问,否则就返回到提示页面。
  55. 在<http>标签中添加登录页面等信息
  56. <http auto-config='true'>
  57. <intercept-url pattern="/login.jsp"access="IS_AUTHENTICATED_ANONYMOUSLY"/>
  58. <intercept-url pattern="/admin.jsp"access="ROLE_ADMIN"/>
  59. <intercept-url pattern="
  60. CREATE TABLE `t_account` (
  61. `id` int(11)NOT NULL,
  62. `username` varchar(255)defaultNULL,
  63. `password` varchar(255)defaultNULL,
  64. `enabled` intdefaultNULL,
  65. PRIMARY KEY (`id`)
  66. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  67. CREATE TABLE `t_role` (
  68. `id` int(11)NOT NULL,
  69. `name` varchar(255)defaultNULL,
  70. `descn` varchar(255)defaultNULL,
  71. PRIMARY KEY (`id`)
  72. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  73. CREATE TABLE `t_account_role` (
  74. `a_id` int(11)NOT NULL,
  75. `r_id` int(11)NOT NULL,
  76. PRIMARY KEY (`a_id`,`r_id`),
  77. KEY `FK1C2BC9332D31C656` (`r_id`),
  78. KEY `FK1C2BC93371CCC630` (`a_id`),
  79. CONSTRAINT `FK1C2BC93384B0A30E` FOREIGN KEY (`a_id`) REFERENCES `t_account` (`id`),
  80. CONSTRAINT `FK1C2BC9332D31C656` FOREIGN KEY (`r_id`) REFERENCES `t_role` (`id`)
  81. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  82. CREATE TABLE `t_module` (
  83. `id` int(11)NOT NULL,
  84. `name` varchar(255)defaultNULL,
  85. `address` varchar(255)defaultNULL,
  86. PRIMARY KEY (`id`)
  87. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  88. CREATE TABLE `t_module_role` (
  89. `m_id` int(11)NOT NULL,
  90. `r_id` int(11)NOT NULL,
  91. PRIMARY KEY (`m_id`,`r_id`),
  92. KEY `FKA713071E2D31C656` (`r_id`),
  93. KEY `FKA713071ED78C9071` (`m_id`),
  94. CONSTRAINT `FKA713071ED78C9071` FOREIGN KEY (`m_id`) REFERENCES `t_module` (`id`),
  95. CONSTRAINT `FKA713071E2D31C656` FOREIGN KEY (`r_id`) REFERENCES `t_role` (`id`)
  96. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  97. insert into t_account values(1,'zhangsan','123',1);
  98. insert into t_account values(2,'lisi','321',1);
  99. insert into t_role values(1,'系统管理员','ROLE_ADMIN');
  100. insert into t_role values(2,'普通用户','ROLE_USER');
  101. insert into t_account_role values(1,2);
  102. insert into t_account_role values(2,1);
  103. insert into t_module values(1,'部门管理','/dept.jsp');
  104. insert into t_module values(2,'人员管理','/emp.jsp');
  105. insert into `t_module_role` values(1,1);
  106. insert into `t_module_role` values(1,2);
  107. insert into `t_module_role` values(2,1);
  108. 1.在自定义的过滤器中获取资源的URL地址和角色名以取代spring配置文件中原有的<intercept-urlpattern="/login.jsp"access="IS_AUTHENTICATED_ANONYMOUSLY"/>
  109. 过滤器代码:
  110. importjava.sql.ResultSet;
  111. importjava.sql.SQLException;
  112. importjava.util.LinkedHashMap;
  113. importjava.util.List;
  114. importjava.util.Map;
  115. importjavax.sql.DataSource;
  116. importorg.springframework.beans.factory.FactoryBean;
  117. importorg.springframework.jdbc.core.support.JdbcDaoSupport;
  118. importorg.springframework.jdbc.object.MappingSqlQuery;
  119. importorg.springframework.security.ConfigAttributeDefinition;
  120. importorg.springframework.security.ConfigAttributeEditor;
  121. importorg.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;
  122. importorg.springframework.security.intercept.web.FilterInvocationDefinitionSource;
  123. importorg.springframework.security.intercept.web.RequestKey;
  124. importorg.springframework.security.util.AntUrlPathMatcher;
  125. importorg.springframework.security.util.UrlMatcher;
  126. publicclassJdbcFilterInvocationDefinitionSourceFactoryBean
  127. extendsJdbcDaoSupport implementsFactoryBean {
  128. privateString resourceQuery;
  129. publicbooleanisSingleton() {
  130. returntrue;
  131. }
  132. publicClass getObjectType() {
  133. returnFilterInvocationDefinitionSource.class;
  134. }
  135. publicObject getObject() {
  136. returnnewDefaultFilterInvocationDefinitionSource(this
  137. .getUrlMatcher(),this.buildRequestMap());
  138. }
  139. protectedMap<String, String> findResources() {
  140. ResourceMapping resourceMapping= newResourceMapping(getDataSource(),
  141. resourceQuery);
  142. Map<String, String> resourceMap= newLinkedHashMap<String, String>();
  143. for(Resource resource : (List<Resource>) resourceMapping.execute()){
  144. String url = resource.getUrl();
  145. String role = resource.getRole();
  146. if(resourceMap.containsKey(url)) {
  147. Stringvalue = resourceMap.get(url);
  148. resourceMap.put(url,value + ","+ role);
  149. } else{
  150. resourceMap.put(url,role);
  151. }
  152. }
  153. returnresourceMap;
  154. }
  155. protectedLinkedHashMap<RequestKey, ConfigAttributeDefinition> buildRequestMap(){
  156. LinkedHashMap<RequestKey, ConfigAttributeDefinition> requestMap = null;
  157. requestMap = newLinkedHashMap<RequestKey, ConfigAttributeDefinition>();
  158. ConfigAttributeEditor editor= newConfigAttributeEditor();
  159. Map<String, String> resourceMap= this.findResources();
  160. for(Map.Entry<String, String> entry : resourceMap.entrySet()){
  161. RequestKey key = newRequestKey(entry.getKey(), null);
  162. editor.setAsText(entry.getValue());
  163. requestMap.put(key,
  164. (ConfigAttributeDefinition)editor.getValue());
  165. }
  166. returnrequestMap;
  167. }
  168. protectedUrlMatcher getUrlMatcher() {
  169. returnnewAntUrlPathMatcher();
  170. }
  171. publicvoidsetResourceQuery(String resourceQuery) {
  172. this.resourceQuery= resourceQuery;
  173. }
  174. privateclassResource {
  175. privateString url;
  176. privateString role;
  177. publicResource(String url, String role) {
  178. this.url= url;
  179. this.role= role;
  180. }
  181. publicString getUrl() {
  182. returnurl;
  183. }
  184. publicString getRole() {
  185. returnrole;
  186. }
  187. }
  188. privateclassResourceMapping extendsMappingSqlQuery {
  189. protectedResourceMapping(DataSource dataSource,
  190. String resourceQuery) {
  191. super(dataSource,resourceQuery);
  192. compile();
  193. }
  194. protectedObject mapRow(ResultSet rs, intrownum)
  195. throwsSQLException {
  196. String url = rs.getString(1);
  197. String role = rs.getString(2);
  198. Resource resource = newResource(url, role);
  199. returnresource;
  200. }
  201. }
  202. }
  203. 将自定义的过滤器放入到原有的spring security过滤器链中(在spring配置文件中配置)
  204. 定义自定义过滤器(sql语句用于查询资源的URL地址 如:/index.jsp 和角色名 如ROLE_USER)
  205. <beans:bean id="filterInvocationDefinitionSource"
  206. class="com.lovo.JdbcFilterInvocationDefinitionSourceFactoryBean">
  207. <beans:property name="dataSource"ref="dataSource"/>
  208. <beans:property name="resourceQuery"value="
  209. select m.address,r.descn
  210. from t_module_role mr
  211. join t_module m on mr.m_id=m.id
  212. join t_role r on mr.r_id=r.id;
  213. "/>
  214. </beans:bean>
  215. 将自定义过滤器放入过滤器链中
  216. <beans:bean id="filterSecurityInterceptor"
  217. class="org.springframework.security.intercept.web.FilterSecurityInterceptor"autowire="byType">
  218. <custom-filter before="FILTER_SECURITY_INTERCEPTOR"/>
  219. <beans:property name="objectDefinitionSource"ref="filterInvocationDefinitionSource"/>
  220. </beans:bean>
  221. 注意:FilterSecurityInterceptor过滤器会向request中写入一个标记,用于标记是否已经控制了当前请求,以避免对同一请求多次处理,导致第2个FilterSecurityInterceptor不会再次执行。
  222. 在<http>中不需要再定义<intercept-url>,如下:
  223. <http auto-config='true'>
  224. <form-login login-page="/login.jsp"
  225. authentication-failure-url="/error.jsp"
  226. default-target-url="/index.jsp"/>
  227. </http>
  228. 自定义的过滤器就从配置文件中读取sql,查询结果就是角色和资源,用户登录时就在session中保存了用户的角色。
  229. 注意:intercept-url的先后顺序,spring security使用第一个能匹配的intercept-url标签进行权限控制。
  230. 现在intercept-url来源于数据库,所以在sql查询时注意角色和资源的顺序。
  231. 建议在角色和资源的中间表中添加1个字段用于标识顺序,(按从严到宽的顺序)
  232. 表结构修改如下:
  233. CREATE TABLE `t_module_role` (
  234. `m_id` int(11)NOT NULL,
  235. `r_id` int(11)NOT NULL,
  236. `priority` int(11)defaultNULL,
  237. PRIMARY KEY (`m_id`,`r_id`),
  238. KEY `FKA713071E2D31C656` (`r_id`),
  239. KEY `FKA713071ED78C9071` (`m_id`),
  240. CONSTRAINT `FKA713071ED78C9071` FOREIGN KEY (`m_id`) REFERENCES `t_module` (`id`),
  241. CONSTRAINT `FKA713071E2D31C656` FOREIGN KEY (`r_id`) REFERENCES `t_role` (`id`)
  242. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  243. 数据如下:
  244. insert into t_account values(1,'zhangsan','123',1);
  245. insert into t_account values(2,'lisi','321',1);
  246. insert into t_role values(1,'系统管理员','ROLE_ADMIN');
  247. insert into t_role values(2,'普通用户','ROLE_USER');
  248. insert into t_account_role values(1,2);
  249. insert into t_account_role values(2,1);
  250. insert into t_module values(1,'部门管理','/dept.jsp');
  251. insert into t_module values(2,'人员管理','/emp.jsp');
  252. insert into `t_module_role` values(1,1,3);
  253. insert into `t_module_role` values(1,2,2);
  254. insert into `t_module_role` values(2,1,1);
  255. 自定义过滤器修改如下:
  256. <beans:bean id="filterInvocationDefinitionSource"
  257. class="com.lovo.JdbcFilterInvocationDefinitionSourceFactoryBean">
  258. <beans:property name="dataSource"ref="dataSource"/>
  259. <beans:property name="resourceQuery"value="
  260. select m.address,r.descn
  261. from t_module_role mr
  262. join t_module m on mr.m_id=m.id
  263. join t_role r on mr.r_id=r.id
  264. order by mr.priority
  265. "/>
  266. </beans:bean>
  267. 如果持久层使用的是hibernate,那么只需要给自定义过滤器注入1个hibernateTemplate.
  268. 在自定义过滤器中就不需要再使用mapRow的方式。而是直接获取url和角色名即可。
  269. 例如:
  270. publicclassModuleFilter implementsFactoryBean {
  271. privateString resourceQuery;
  272. publicbooleanisSingleton() {
  273. returntrue;
  274. }
  275. publicClass getObjectType() {
  276. returnFilterInvocationDefinitionSource.class;
  277. }
  278. publicObject getObject() {
  279. returnnewDefaultFilterInvocationDefinitionSource(this
  280. .getUrlMatcher(),this.buildRequestMap());
  281. }
  282. protectedMap<String, String> findResources() {
  283. ResourceMapping resourceMapping= newResourceMapping();
  284. Map<String, String> resourceMap= newLinkedHashMap<String, String>();
  285. for(Resource resource : (List<Resource>) resourceMapping.execute()){
  286. String url = resource.getUrl();
  287. String role = resource.getRole();
  288. if(resourceMap.containsKey(url)) {
  289. Stringvalue = resourceMap.get(url);
  290. resourceMap.put(url,value + ","+ role);
  291. } else{
  292. resourceMap.put(url,role);
  293. }
  294. }
  295. returnresourceMap;
  296. }
  297. protectedLinkedHashMap<RequestKey, ConfigAttributeDefinition> buildRequestMap(){
  298. LinkedHashMap<RequestKey, ConfigAttributeDefinition> requestMap = null;
  299. requestMap = newLinkedHashMap<RequestKey, ConfigAttributeDefinition>();
  300. ConfigAttributeEditor editor= newConfigAttributeEditor();
  301. Map<String, String> resourceMap= this.findResources();
  302. for(Map.Entry<String, String> entry : resourceMap.entrySet()){
  303. RequestKey key = newRequestKey(entry.getKey(), null);
  304. editor.setAsText(entry.getValue());
  305. requestMap.put(key,
  306. (ConfigAttributeDefinition)editor.getValue());
  307. }
  308. returnrequestMap;
  309. }
  310. protectedUrlMatcher getUrlMatcher() {
  311. returnnewAntUrlPathMatcher();
  312. }
  313. publicvoidsetResourceQuery(String resourceQuery) {
  314. this.resourceQuery= resourceQuery;
  315. }
  316. privateclassResource {
  317. privateString url;
  318. privateString role;
  319. publicResource(String url, String role) {
  320. this.url= url;
  321. this.role= role;
  322. }
  323. publicString getUrl() {
  324. returnurl;
  325. }
  326. publicString getRole() {
  327. returnrole;
  328. }
  329. }
  330. privateclassResourceMapping{
  331. publicList<Resource> execute(){
  332. List<Resource> rlist = newArrayList<Resource>();
  333. List<Role> list = hibernateTemplate.find(resourceQuery);
  334. for(inti=0;i<list.size();i++){
  335. Role role = list.get(i);
  336. Set<Module> set = role.getModuleSet();
  337. Iterator<Module> it =set.iterator();
  338. while(it.hasNext()){
  339. Module m = it.next();
  340. Resource re = newResource(m.getUrl(),role.getDescn());
  341. rlist.add(re);
  342. }
  343. }
  344. returnrlist;
  345. }
  346. }
  347. publicvoidsetHibernateTemplate(HibernateTemplate hibernateTemplate) {
  348. this.hibernateTemplate= hibernateTemplate;
  349. }
  350. privateHibernateTemplate hibernateTemplate;
  351. }
  352. 而从灵活性的角度考虑,把hql写在配置文件中
  353. <beans:bean id="moduleFilter"class="com.lovo.ModuleFilter">
  354. <beans:property name="resourceQuery"
  355. value="fromcom.lovo.po.Role order by ind">
  356. </beans:property>
  357. <beans:property name="hibernateTemplate"ref="hibernateTemplate">
  358. </beans:property>
  359. </beans:bean>
  360. 问题:系统只会在初始化的时候从数据库中加载信息。无法识别数据库中信息的改变。
  361. 解决:每个jsp页面上重新内存
  362. 代码:每个jsp页面include如下代码:
  363. <%@page import="org.springframework.context.ApplicationContext"%>
  364. <%@page import="org.springframework.web.context.support.WebApplicationContextUtils"%>
  365. <%@page import="org.springframework.beans.factory.FactoryBean"%>
  366. <%@page import="org.springframework.security.intercept.web.FilterSecurityInterceptor"%>
  367. <%@page import="org.springframework.security.intercept.web.FilterInvocationDefinitionSource"%>
  368. <%
  369. ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(application);
  370. FactoryBean factoryBean = (FactoryBean)ctx.getBean("&自定义过滤器的id");
  371. FilterInvocationDefinitionSource fids = (FilterInvocationDefinitionSource) factoryBean.getObject();
  372. FilterSecurityInterceptor filter = (FilterSecurityInterceptor) ctx.getBean("filterSecurityInterceptor");
  373. filter.setObjectDefinitionSource(fids);
  374. %>
  375. 控制用户信息
  376. 用户密码MD5加密:
  377. <authentication-provider>
  378. <password-encoder hash="md5"/>
  379. <jdbc-user-service data-source-ref="dataSource"
  380. users-by-username-query="selectusername,password,enabled from t_account where username=?"
  381. authorities-by-username-query="selecta.username,r.descn from t_account_role ar join
  382. t_account a on ar.a_id=a.id join t_role r on ar.r_id=r.id where a.username=?"/>
  383. </authentication-provider>
  384. 盐值加密
  385. <authentication-provider>
  386. <password-encoder hash="md5">
  387. <salt-source user-property="username"/>
  388. </password-encoder>
  389. <jdbc-user-service data-source-ref="dataSource"
  390. users-by-username-query="selectusername,password,enabled from t_account where username=?"
  391. authorities-by-username-query="selecta.username,r.descn from t_account_role ar join
  392. t_account a on ar.a_id=a.id join t_role r on ar.r_id=r.id where a.username=?"/>
  393. </authentication-provider>
  394. 用户信息缓存,使用spring内置的ehCache实现
  395. <beans:bean id="cacheManager"class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"></beans:bean>
  396. <beans:bean id="userEhCache"class="org.springframework.cache.ehcache.EhCacheFactoryBean">
  397. <beans:property name="cacheManager"ref="cacheManager"></beans:property>
  398. <beans:property name="cacheName"value="userCache"></beans:property>
  399. </beans:bean>
  400. <beans:bean id="userCache"class="org.springframework.security.providers.dao.cache.EhCacheBasedUserCache">
  401. <beans:property name="cache"ref="userEhCache"></beans:property>
  402. </beans:bean>
  403. 在src目录下新建ehcache.xml
  404. <ehcache>
  405. <diskStore path="java.io.tmpdir"/>
  406. <defaultCache
  407. maxElementsInMemory="1000"
  408. eternal="false"
  409. timeToIdleSeconds="120"
  410. timeToLiveSeconds="120"
  411. overflowToDisk="true"
  412. />
  413. <cache
  414. name="userCache"
  415. maxElementsInMemory="100"
  416. eternal="false"
  417. timeToIdleSeconds="600"
  418. timeToLiveSeconds="3600"
  419. overflowToDisk="true"
  420. />
  421. </ehcache>
  422. 在程序中获取用户信息
  423. UserDetails ud = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
  424. String name = ud.getUsername();
  425. String pwd = ud.getPassword();
  426. GrantedAuthority[] ga = ud.getAuthorities();
  427. System.out.println(name + ","+ pwd);
  428. for(GrantedAuthorityg : ga){
  429. System.out.println(g.getAuthority());
  430. }
  431. 自定义访问拒绝页面
  432. <http auto-config='true'access-denied-page="/error.jsp">
  433. 访问用户以带参形式访问
  434. 在资源URL地址后加*
  435. insert into t_module values(1,'部门管理','/dept.jsp*');
  436. insert into t_module values(2,'人员管理','/emp.jsp*');
  437. 自定义用户接口实现
  438. 由于将sql写在配置文件中只适用于小型系统,而且不灵活,在大型系统或实体关系复杂时需要自定义用户实现的接口。
  439. 自定义用户实现需要实现2个接口。
  440. UserDetails:实体类需要实现的接口。
  441. UserDetailsService:实体管理类需要实现的接口。
  442. 配置:
  443. <!-- 实体管理类 -->
  444. <beans:bean id="userManager"class="com.lovo.UserManager"></beans:bean>
  445. <authentication-provider user-service-ref="userManager">
  446. <!--
  447. <jdbc-user-service data-source-ref="dataSource"
  448. users-by-username-query="selectusername,password,enabled from t_account where username=?"
  449. authorities-by-username-query="selecta.username,r.descn from t_account_role ar join
  450. t_account a on ar.a_id=a.id join t_role r on ar.r_id=r.id where a.username=?"/>
  451. -->
  452. </authentication-provider>
  453. 原有的通过固定sql语句的获取方式可以抛弃,改为使用user-service-ref属性注入的bean来实现对用户及用户所
  454. 拥有的资源的查找。
  455. 用户注销
  456. 注销功能由过滤器org.springframework.security.ui.logout.LogoutFilter负责完成。
  457. <a href="${pageContext.request.contextPath}/j_spring_security_logout">注销</a>
  458. 被注销的用户就是当前session中保存的用户,注销后页面自动重定向到default-target-url所指定的页面
  459. spring security过滤器体系
  460. spring security的一系列功能都是由一串过滤器来完成的。在spring配置文件中配置的<http>标签实际上就是
  461. 起到默认的过滤器进行声明和配置的作用。
  462. 管理会话
  463. 当项目中要求不能使用同一个账号同时登陆,按以下步骤实施
  464. 1.web.xml添加1个监听器
  465. <listener>
  466. <listener-class>org.springframework.security.ui.session.HttpSessionEventPublisher</listener-class>
  467. </listener>
  468. 2.spring配置文件中的<http>标签添加子标签<concurrent-session-control/>
  469. <http auto-config='true'access-denied-page="/noview.jsp">
  470. <form-login login-page="/login.jsp"authentication-failure-url="/error.jsp"default-target-url="/index.jsp"/>
  471. <concurrent-session-control/>
  472. </http>
  473. <concurrent-session-control/>会产生1org.springframework.security.concurrent.ConcurrentSessionFilter
  474. 并放在过滤器链的最前面。
  475. 默认情况下,使用同一账号后登陆的用户会踢出先登陆的用户。
  476. 如果想禁止第2个用户登录,则设置
  477. <concurrent-session-control exception-if-maximum-exceeded="true"/>
  478. 对方法级的权限控制
  479. 1.添加依赖包 cglib-nodep-2.1_3.jaraspectjweaver.jar aspectjrt.jar
  480. 2.利用<global-method-security>设置需要保护的方法及可以调用的权限
  481. <global-method-security>
  482. <protect-pointcut access="ROLE_ADMIN,ROLE_USER"expression="execution(* com.lovo.bo.AccountBo.get*(..))"/>
  483. <protect-pointcut access="ROLE_ADMIN"expression="execution(* com.lovo.bo.AccountBo.create*(..))"/>
  484. </global-method-security>
  485. 利用注解同样可以实现方法级的保护
  486. 需要spring-security-core-tiger.jar包
  487. 启用注解保护
  488. <global-method-security secured-annotations="enabled"/>
  489. @Secured({"ROLE_ADMIN","ROLE_USER"})
  490. publicvoidgetOneAccount();
  491. 拥护ROLE_ADMIN或ROLE_USER权限的用户可以调用该方法
  492. SecurityContext安全上下文
  493. SecurityContext securityContext = SecurityContextHolder.getContext();
  494. SecurityContext中保存着实现了Authentication 接口的对象,如果用户尚未通过
  495. 认证,那么SecurityContext.getAuthenticaiton()方法就会返回null
  496. 注意,如果使用了匿名用户,SecurityContext.getAuthenticaiton()返回的不是null.
  497. 只有在未启用过滤器链的情况下,SecurityContext.getAuthenticaiton()才返回空。
  498. 验证管理器
  499. 验证管理器用来识别用户的身份。使用命名空间会自动注册一个验证管理器的bean.
  500. 是org.springframework.security.providers.ProviderManager类的一个对象。
  501. 如果要在其他的bean中要引用这个验证管理器,则给这个验证管理器取一个别名。
  502. <authentication-manager alias="authenticationManager"/>
  503. 如果要采用其他的类来完成验证管理器的功能,可以使用以下标签
  504. <beans:bean id="abc"class="org.springframework.security.providers.dao.DaoAuthenticationProvider">
  505. <custom-authentication-provider/>
  506. </beans:bean>
  507. 访问决策管理器
  508. 当使用命名空间配置时,默认的AccessDecisionManager实例会自动注册。
  509. 默认的策略是使用一个AffirmativeBased作为AccessDecisionManager(访问决策管理器),投票者是RoleVoter
  510. AuthenticatedVote
  511. <global-method-security secured-annotations="enabled"access-decision-manager-ref="accessDecisionManager">
  512. </global-method-security>
  513. 利用访问决策管理器对方法进行保护
  514. <beans:bean id="filterSecurityInterceptor"class="org.springframework.security.intercept.web.FilterSecurityInterceptor"autowire="byType">
  515. <custom-filter before="FILTER_SECURITY_INTERCEPTOR"/>
  516. <beans:property name="objectDefinitionSource"ref="moduleFilter"/>
  517. <beans:property name="accessDecisionManager"ref="accessDecisionManager"></beans:property>
  518. </beans:bean>
  519. 向过滤器注入访问决策管理器
  520. 不管是MethodSecurityInterceptor还是FilterSecurityInterceptor都使用authenticationManager和accessDecisionManager属性用于验证用户,并且都是通过使用 objectDefinitionSource属性来定义受保护的资源。不同的是过滤器安全拦截器将URL资源与权限关联,而方法安全拦截器将业务方法与权限关联。
  521. objectDefinitionSource属性需要注入的就是 org.springframework.security.intercept.ObjectDefinitionSource这个接口的实现类。
  522. 该接口中有一个方法是:
  523. ConfigAttributeDefinition getAttributes(Object object) throwsIllegalArgumentException;
  524. 参数实际类型是org.springframework.security.intercept.web.FilterInvocation
  525. 这个方法用于获取保护资源对应的权限信息,返回一个ConfigAttributeDefinition对象。
  526. ConfigAttributeDefinition对象内部维护1个列表,安全拦截器就通过调用getAttributes方法来获取ConfigAttributeDefinition对象,并将该对象和当前用户拥有的Authentication对象传递给 accessDecisionManager(访问决策管理器)
  527. 访问决策管理器在将其传递给具体实现类维护的投票者,这些投票者从ConfigAttributeDefinition对象中获取这个存放了访问保护资源需要的权限信息的列表,然后遍历这个列表并与Authentication对象中GrantedAuthority[]数据中的用户权限信息进行匹配,如果匹配成功,投票者就会投赞成票,否则就投反对票,最后访问决策管理器来统计这些投票决定用户是否能访问该资源。
  528. FilterInvocationDefinitionSource接口和MethodDefinitionSource接口继承自ObjectDefinitionSource接口,并提供了2个默认实现类用以从配置文件读取权限信息。
  529. 是DefaultFilterInvocationDefinitionSource和 DelegatingMethodDefinitionSource两个类,如果需要从其他数据来源读取则需要实现FilterInvocationDefinitionSource接口和MethodDefinitionSource接口。
  530. 自定义的过滤器必须注入 objectDefinitionSource,accessDecisionManager,authenticationManager3个属性。
  531. FilterInvocationDefinitionSource接口getAttributes实现思路
  532. 1.获取客户端访问的url地址。
  533. 2.将该url地址和数据库中存储的url地址匹配,找到所有有权访问该地址的权限(角色)名字。
  534. 3.把所有的权限名利用ConfigAttributeEditor类封装成ConfigAttributeDefinition对象
  535. 简单例子:
  536. publicclassMyFilter extendsHibernateDaoSupport implementsFilterInvocationDefinitionSource,FactoryBean{
  537. //保存权限信息(能够访问当前资源的角色名)
  538. privateList<String> roleNameList = newArrayList<String>();
  539. publicConfigAttributeDefinition getAttributes(Object object) throwsIllegalArgumentException {
  540. FilterInvocation fi = (FilterInvocation)object;
  541. String requeatUrl = fi.getRequestUrl(); //获取客户端访问的url地址
  542. requeatUrl = requeatUrl.toLowerCase(); //将url地址全部转换成小写
  543. if(requeatUrl.indexOf("?")!= -1){ //过滤请求参数
  544. requeatUrl = requeatUrl.substring(0,requeatUrl.indexOf("?"));
  545. }
  546. //在数据库中查找能够访问此url地址的角色
  547. List<Module> list = this.getHibernateTemplate().find("fromcom.lovo.po.Module where url like ?",requeatUrl+"%");
  548. if(list.size()<= 0){
  549. returnnull;
  550. }
  551. String s = "";
  552. Module module = (Module)list.get(0);
  553. Set<Role> set = module.getRoleSet();
  554. roleNameList.clear();
  555. for(Rolerole : set){
  556. roleNameList.add(role.getDescn());
  557. s += role.getDescn() + ",";
  558. }
  559. s = s.substring(0,(s.length()- 1)); //将所有的角色名(ROLE_开头)拼接为一个字符串。
  560. ConfigAttributeEditor editer = newConfigAttributeEditor();
  561. editer.setAsText(s);
  562. return(ConfigAttributeDefinition) editer.getValue(); //将包含所有角色名(ROLE_开头)的字符串转换为ConfigAttributeDefinition对象。
  563. }
  564. publicCollection getConfigAttributeDefinitions() {
  565. returnCollections.unmodifiableCollection(this.roleNameList);
  566. }
  567. publicbooleansupports(Class clazz) {
  568. returnFilterInvocation.class.isAssignableFrom(clazz);
  569. }
  570. publicObject getObject() throwsException {
  571. returnthis;
  572. }
  573. publicClass getObjectType() {
  574. returnFilterInvocationDefinitionSource.class;
  575. }
  576. publicbooleanisSingleton() {
  577. // TODO Auto-generated methodstub
  578. returntrue;
  579. }
  580. }
  581. 方法保护的原理是通过aop来实现的。
  582. <aop:config>
  583. <aop:pointcut expression="execution(*com.lovo.bo.face.*.*(..))" id="me"/>
  584. <aop:advisor advice-ref="methodSecurityInterceptor"pointcut-ref="me"/>
  585. </aop:config>
  586. 通知是1个实现了MethodSecurityInterceptor接口的类
  587. methodSecurityInterceptor是spring中的一个类
  588. <beans:bean id="methodSecurityInterceptor"class="org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor">
  589. <beans:property name="objectDefinitionSource"ref="methodFilter"/>
  590. <beans:property name="accessDecisionManager"ref="accessDecisionManager"></beans:property>
  591. <beans:property name="authenticationManager"ref="authenticationManager"></beans:property>
  592. </beans:bean>
  593. accessDecisionManager属性注入访问决策管理器,authenticationManager属性注入验证管理器。
  594. objectDefinitionSource属性注入1个实现了MethodDefinitionSource接口的类。
  595. <beans:bean id="methodFilter"class="com.lovo.method.MethodFilter">
  596. <beans:property name="hibernateTemplate"ref="hibernateTemplate"></beans:property>
  597. </beans:bean>
  598. 这个类的作用是根据用户调用的方法,找到相应的角色,
  599. publicclassMethodFilter extendsHibernateDaoSupport implementsMethodDefinitionSource{
  600. privateList<String> roleList = newArrayList<String>();
  601. publicConfigAttributeDefinition getAttributes(Method method, ClasstargetClass) {
  602. returnnull;
  603. }
  604. publicConfigAttributeDefinition getAttributes(Object object)
  605. throwsIllegalArgumentException {
  606. ConfigAttributeEditor editor = newConfigAttributeEditor();
  607. String s = "";
  608. ReflectiveMethodInvocation rmi = (ReflectiveMethodInvocation)object;
  609. String methodName = rmi.getThis().getClass().getName() + "." + rmi.getMethod().getName();
  610. System.out.println(methodName);
  611. List<Module> list = this.getHibernateTemplate().find("fromcom.lovo.po.Module where url like ?",methodName);
  612. if(list.size()== 0){
  613. returnnull;
  614. }
  615. roleList.clear();
  616. for(inti=0;i<list.size();i++){
  617. Module module = list.get(i);
  618. Set<Role> roleSet = module.getRoleSet();
  619. Iterator<Role> it = roleSet.iterator();
  620. while(it.hasNext()){
  621. Role role = it.next();
  622. s += role.getDescn() + ",";
  623. roleList.add(role.getDescn());
  624. }
  625. }
  626. s = s.substring(0,(s.length()- 1));
  627. editor.setAsText(s);
  628. editor.getValue();
  629. return(ConfigAttributeDefinition) editor.getValue();
  630. }
  631. publicCollection getConfigAttributeDefinitions() {
  632. returnroleList;
  633. }
  634. publicbooleansupports(Class clazz) {
  635. returntrue;
  636. }
  637. }
原创粉丝点击