打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
spring security 3.1配置过程从简单到复杂详细配置

  1. <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">废话不多说,直接进入主题。此文章会慢慢跟新。我就在此做一个记录。以后方便自己查阅。</span>  

spring security3.1的文档可以去官网下,里面有jar包和说明文档

关于Spring Security3.1的配置网上也很多,但是看看是没有用的,还是需要自己慢慢研究搭环境。

先从最简单的开始说起。


测试项目在:http://download.csdn.net/detail/cctv99999999/7112505    可以下载,测试通过可用。数据库采用mysql。需要的同学可以去看一下。jar包已经配好。


环境搭建

首先我们先把环境搭起来,

我用的是spring mvc+spring securiity3.1,先要配置web.xml文件,

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app version="3.0"   
  3.     xmlns="http://java.sun.com/xml/ns/javaee"   
  4.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
  5.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   
  6.     http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">  
  7.       
  8.       
  9.       
  10.     <context-param>  
  11.         <param-name>contextConfigLocation</param-name>  
  12.         <param-value>/WEB-INF/security/*.xml</param-value>  
  13.     </context-param>  
  14.     <listener>  
  15.         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  16.     </listener>  
  17.       
  18.     <servlet>  
  19.         <servlet-name>dispatcher</servlet-name>  
  20.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  21.         <init-param>  
  22.             <param-name>contextConfigLocation</param-name>  
  23.             <param-value>/WEB-INF/security/dispatcher-servlet.xml</param-value>  
  24.         </init-param>  
  25.         <load-on-startup>1</load-on-startup>  
  26.     </servlet>  
  27.     <servlet-mapping>  
  28.         <servlet-name>dispatcher</servlet-name>  
  29.         <url-pattern>*.do</url-pattern>  
  30.     </servlet-mapping>  
  31.  <!-- 权限 -->    
  32.      <filter>  
  33.          <filter-name>springSecurityFilterChain</filter-name>  
  34.          <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
  35.      </filter>  
  36.   
  37.     <filter-mapping>  
  38.       <filter-name>springSecurityFilterChain</filter-name>  
  39.       <url-pattern>/*</url-pattern>  
  40.     </filter-mapping>  
  41.   
  42. </web-app>  

spring-security.xml是用来配置security的,dispatcher-servlet.xml是用来配置spring mvc的,

spring mvc的配置很简单,我上面都打了注释。

  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:mvc="http://www.springframework.org/schema/mvc"  
  5.        xmlns:p="http://www.springframework.org/schema/p"  
  6.        xmlns:context="http://www.springframework.org/schema/context"  
  7.        xmlns:aop="http://www.springframework.org/schema/aop"  
  8.        xmlns:tx="http://www.springframework.org/schema/tx"  
  9.        xsi:schemaLocation="http://www.springframework.org/schema/beans  
  10.             http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  11.             http://www.springframework.org/schema/context   
  12.             http://www.springframework.org/schema/context/spring-context-3.0.xsd  
  13.             http://www.springframework.org/schema/aop   
  14.             http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
  15.             http://www.springframework.org/schema/tx   
  16.             http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
  17.             http://www.springframework.org/schema/mvc   
  18.             http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">  
  19.     <!-- 
  20.         使Spring支持自动检测组件,如注解的Controller 
  21.     -->  
  22.    
  23.     <context:component-scan base-package="com"/>  
  24.     <aop:aspectj-autoproxy/> <!-- 开启AOP  -->  
  25.     
  26.     
  27.      
  28.     <bean id="viewResolver"  
  29.           class="org.springframework.web.servlet.view.InternalResourceViewResolver"  
  30.           p:prefix="/jsp/"  
  31.           p:suffix=".jsp"   
  32.           />   
  33.             
  34.             
  35.             
  36.      <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">      
  37.         <property name="messageConverters">      
  38.             <list >      
  39.                 <ref bean="mappingJacksonHttpMessageConverter" />      
  40.             </list>      
  41.         </property>      
  42.     </bean>  
  43.       
  44.       
  45.     <bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />      
  46.     <!-- 数据库连接配置 -->  
  47.     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">  
  48.         <property name="driverClass" value="com.mysql.jdbc.Driver"></property>  
  49.         <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/power"></property>  
  50.         <property name="user" value="root"></property>  
  51.         <property name="password" value="516725"></property>  
  52.         <property name="minPoolSize" value="10"></property>  
  53.         <property name="MaxPoolSize" value="50"></property>  
  54.         <property name="MaxIdleTime" value="60"></property><!-- 最少空闲连接 -->  
  55.         <property name="acquireIncrement" value="5"></property><!-- 当连接池中的连接耗尽的时候 c3p0一次同时获取的连接数。 -->  
  56.         <property name="TestConnectionOnCheckout" value="true" ></property>  
  57.     </bean>  
  58.     <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">  
  59.         <property name="dataSource">  
  60.             <ref local="dataSource"/>  
  61.         </property>  
  62.     </bean>  
  63.     <!-- 事务申明 -->  
  64.     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  65.         <property name="dataSource" >  
  66.             <ref local="dataSource"/>  
  67.         </property>  
  68.     </bean>  
  69.     <!-- Aop切入点 -->  
  70.     <aop:config>  
  71.         <aop:pointcut expression="within(com.ucs.security.dao.*)" id="serviceOperaton"/>  
  72.         <aop:advisor advice-ref="txadvice" pointcut-ref="serviceOperaton"/>  
  73.     </aop:config>  
  74.     <tx:advice id="txadvice" transaction-manager="transactionManager">  
  75.         <tx:attributes>  
  76.             <tx:method name="delete*" propagation="REQUIRED"/>  
  77.         </tx:attributes>  
  78.     </tx:advice>  
  79. </beans>  

这时候去配置security,我们可以去查阅官方的文档资料。主要的配置在这网页上:

spring-security-3.1.4.RELEASE-dist/spring-security-3.1.4.RELEASE/docs/reference/ns-config.html,在3,。1中我们可以看大下面的基础配置文件



我们可以将第二段段基础配置文件复制到spring-security.xml中,如下

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans:beans xmlns="http://www.springframework.org/schema/security"  
  3.   xmlns:beans="http://www.springframework.org/schema/beans"  
  4.   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  5.   xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6.            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7.            http://www.springframework.org/schema/security  
  8.            http://www.springframework.org/schema/security/spring-security-3.1.xsd">  
  9.    
  10. </beans:beans>     

这样环境基本就搭建好了。

硬编码的简单配置

在春天spring-securoty.xml配置文件中添加如下配置:

  1. <!-- 对所有页面进行拦截,需要ROLE_USER权限-->  
  2.  <http auto-config='true'>  
  3.    <intercept-url pattern="/**" access="ROLE_USER" />  
  4.  </http>  
  5.  <!-- 权限配置 jimi拥有两种权限 bob拥有一种权限-->  
  6. <authentication-manager>  
  7.    <authentication-provider>  
  8.      <user-service>  
  9.        <user name="jimi" password="123" authorities="ROLE_USER, ROLE_ADMIN" />  
  10.        <user name="bob" password="456" authorities="ROLE_USER" />  
  11.      </user-service>  
  12.    </authentication-provider>  
  13.  </authentication-manager>  
然后基本就可以启动了。security会自动生成登录页面的。http://localhost:8080/spring-security/spring_security_login,这是自动生成的网页代码



他有默认的请求路径和post字段,如果我们自己写登录页面这些都是不能改的。


第二种是通过查数据库来找到用户拥有的权限来控制用户的访问:

建表sql 数据库采用mysql

  1. CREATE TABLE `user` (  
  2.   
  3. `Id` int(11) NOT NULL auto_increment,  
  4.   
  5. `logname` varchar(255) default NULL,  
  6.   
  7. `password` varchar(255) default NULL,  
  8.   
  9. `role_ids` varchar(255) default NULL,  
  10.   
  11. PRIMARY KEY  (`Id`)  
  12. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  

修改spring-security.xml文件:

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans:beans xmlns="http://www.springframework.org/schema/security"  
  3.   xmlns:beans="http://www.springframework.org/schema/beans"  
  4.   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  5.   xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6.            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7.            http://www.springframework.org/schema/security  
  8.            http://www.springframework.org/schema/security/spring-security-3.1.xsd">  
  9.   
  10.     <!-- 启用方法控制访问权限  用于直接拦截接口上的方法,拥有权限才能访问此方法-->  
  11.     <global-method-security jsr250-annotations="enabled"/>  
  12.     <!-- 自己写登录页面,并且登陆页面不拦截 -->  
  13.     <http pattern="/jsp/login.jsp" security="none" />  
  14.       
  15.     <!-- 配置拦截页面  -->                              <!-- 启用页面级权限控制 使用表达式 -->  
  16.     <http auto-config='true' access-denied-page="/jsp/403.jsp" use-expressions="true">  
  17.         <intercept-url pattern="/**" access="hasRole('ROLE_USER')" />  
  18.         <!-- 设置用户默认登录页面 -->  
  19.         <form-login login-page="/jsp/login.jsp"/>  
  20.     </http>  
  21.       
  22.     <authentication-manager>  
  23.         <!-- 权限控制 引用 id是myUserDetailsService的server -->  
  24.         <authentication-provider user-service-ref="myUserDetailsService"/>  
  25.     </authentication-manager>  
  26.     
  27. </beans:beans>   

首先编辑自己的登录页面:登录界面中提交的字段必须是security默认的,

  1. <%@ page language="java" contentType="text/html; charset=utf-8"  
  2.     pageEncoding="utf-8"%>  
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  
  4. <html>  
  5. <head>  
  6. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">  
  7. <title>登录界面</title>  
  8. </head>  
  9. <body>  
  10.     <h3>登录界面</h3>  
  11.       
  12.         <form action="/spring-security/j_spring_security_check" method="post">  
  13.             <table>  
  14.                 <tr><td>User:</td><td><input type='text' name='j_username' value=''></td></tr>  
  15.                 <tr><td>Password:</td><td><input type='password' name='j_password'/></td></tr>  
  16.                 <tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>  
  17.             </table>  
  18.         </form>  
  19.       
  20. </body>  
  21. </html>  

user.jsp页面:user页面是ROLE_USER权限界面,其中引用了security标签,在配置文件中 use-expressions="true"代表启用页面控制语言,就是根据不同的权限页面显示该权限应该显示的内容。如果查看网页源代码也是看不到初自己权限以外的内容的。

  1. <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>  
  2. <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>  
  3. <%@ taglib prefix="s" uri="http://www.springframework.org/tags/form" %>  
  4. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  
  5. <html>  
  6. <head>  
  7. <meta http-equiv="Content-Type" content="text/html; utf-8">  
  8. <title>Insert title here</title>  
  9. </head>  
  10. <body>  
  11.     <h5><a href="../j_spring_security_logout">logout</a></h5>  
  12.     <!-- 拥有ROLE_ADMIN权限的才看的到 -->  
  13.     <sec:authorize access="hasRole('ROLE_ADMIN')">  
  14.     <form action="#">  
  15.         账号:<input type="text" /><br/>  
  16.         密码:<input type="password"/><br/>  
  17.         <input type="submit" value="submit"/>  
  18.     </form>  
  19.     </sec:authorize>  
  20.       
  21.     <p/>  
  22.     <sec:authorize access="hasRole('ROLE_USER')">  
  23.     显示拥有ROLE_USER权限的页面<br/>  
  24.     <form action="#">  
  25.         账号:<input type="text" /><br/>  
  26.         密码:<input type="password"/><br/>  
  27.         <input type="submit" value="submit"/>  
  28.     </form>  
  29.       
  30.     </sec:authorize>  
  31.     <p/>  
  32.     <h5>测试方法控制访问权限</h5>  
  33.     <a href="addreport_admin.do">添加报表管理员</a><br/>  
  34.     <a href="deletereport_admin.do">删除报表管理员</a>  
  35. </body>  
  36. </html>  

Controller层

  1. package com.ucs.security.server;  
  2.   
  3.   
  4. import javax.annotation.Resource;  
  5. import javax.servlet.http.HttpServletRequest;  
  6.   
  7. import org.springframework.stereotype.Controller;  
  8. import org.springframework.web.bind.annotation.RequestMapping;  
  9. import org.springframework.web.bind.annotation.ResponseBody;  
  10. import org.springframework.web.servlet.ModelAndView;  
  11.   
  12. import com.ucs.security.face.SecurityTestInterface;  
  13.   
  14. @Controller  
  15. public class SecurityTest {  
  16.     @Resource  
  17.     private SecurityTestInterface dao;  
  18.       
  19.     @RequestMapping(value="/jsp/getinput")//查看最近收入  
  20.     @ResponseBody  
  21.     public boolean getinput(HttpServletRequest req,HttpServletRequest res){  
  22.         boolean b=dao.getinput();  
  23.         return b;  
  24.     }  
  25.       
  26.       
  27.     @RequestMapping(value="/jsp/geoutput")//查看最近支出  
  28.     @ResponseBody  
  29.     public boolean geoutput(HttpServletRequest req,HttpServletRequest res){  
  30.         boolean b=dao.geoutput();  
  31.         return b;  
  32.     }  
  33.       
  34.     @RequestMapping(value="/jsp/addreport_admin")//添加报表管理员  
  35.     @ResponseBody  
  36.     public boolean addreport_admin(HttpServletRequest req,HttpServletRequest res){  
  37.         boolean b=dao.addreport_admin();  
  38.         return b;  
  39.     }  
  40.       
  41.     @RequestMapping(value="/jsp/deletereport_admin")//删除报表管理员  
  42.     @ResponseBody  
  43.     public boolean deletereport_admin(HttpServletRequest req,HttpServletRequest res){  
  44.         boolean b=dao.deletereport_admin();  
  45.         return b;  
  46.     }  
  47.       
  48.     @RequestMapping(value="/jsp/user")//普通用户登录  
  49.     public ModelAndView user_login(HttpServletRequest req,HttpServletRequest res){  
  50.         dao.user_login();  
  51.         return new ModelAndView("user");  
  52.     }  
  53.       
  54. }  

接口: 在配置文件中<global-method-security jsr250-annotations="enabled"/>就是在接口上设置权限,当拥有权限时才能调用方法,没有权限是不能调用方法的,保证了安全性

  1. package com.ucs.security.face;  
  2.   
  3. import javax.annotation.security.RolesAllowed;  
  4.   
  5. import com.ucs.security.pojo.Users;  
  6.   
  7. public interface SecurityTestInterface {  
  8.   
  9.     boolean getinput();  
  10.   
  11.     boolean geoutput();  
  12.     @RolesAllowed("ROLE_ADMIN")//拥有ROLE_ADMIN权限的用户才可进入此方法  
  13.     boolean addreport_admin();  
  14.     @RolesAllowed("ROLE_ADMIN")  
  15.     boolean deletereport_admin();  
  16.       
  17.     Users findbyUsername(String name);  
  18.     @RolesAllowed("ROLE_USER")  
  19.     void user_login();  
  20.   
  21. }  

实现类 dao

  1. package com.ucs.security.dao;  
  2. import java.sql.SQLException;  
  3. import javax.annotation.Resource;  
  4. import org.apache.log4j.Logger;  
  5. import org.springframework.jdbc.core.JdbcTemplate;  
  6. import org.springframework.jdbc.core.RowCallbackHandler;  
  7. import org.springframework.stereotype.Repository;  
  8. import com.ucs.security.face.SecurityTestInterface;  
  9. import com.ucs.security.pojo.Users;  
  10.   
  11. @Repository("SecurityTestDao")  
  12. public class SecurityTestDao implements SecurityTestInterface{  
  13.     Logger log=Logger.getLogger(SecurityTestDao.class);  
  14.       
  15.     @Resource  
  16.     private JdbcTemplate jdbcTamplate;  
  17.     public boolean getinput() {  
  18.         log.info("getinput");  
  19.         return true;  
  20.     }  
  21.       
  22.     public boolean geoutput() {  
  23.         log.info("geoutput");  
  24.         return true;  
  25.     }  
  26.   
  27.     public boolean addreport_admin() {  
  28.         log.info("addreport_admin");  
  29.         return true;  
  30.     }  
  31.   
  32.     public boolean deletereport_admin() {  
  33.         log.info("deletereport_admin");  
  34.         return true;  
  35.     }  
  36.   
  37.   
  38.     public Users findbyUsername(String name) {  
  39.         final Users users = new Users();    
  40.         jdbcTamplate.query("SELECT * FROM USER WHERE logname = ?",    
  41.                             new Object[] {name},    
  42.                             new RowCallbackHandler() {    
  43.                                 @Override  
  44.                                 public void processRow(java.sql.ResultSet rs)  
  45.                                         throws SQLException {  
  46.                                  users.setName(rs.getString("logname"));  
  47.                                  users.setPassword(rs.getString("password"));  
  48.                                  users.setRole(rs.getString("role_ids"));  
  49.                                 }    
  50.                             });    
  51.         log.info(users.getName()+"    "+users.getPassword()+"    "+users.getRole());  
  52.         return users;  
  53.     }  
  54.   
  55.       
  56.           
  57.     @Override  
  58.     public void user_login() {  
  59.         log.info("拥有ROLE_USER权限的方法访问:user_login");  
  60.           
  61.     }  
  62.   
  63. }  

下面就是重要的一个类:MyUserDetailsService这个类需要去实现UserDetailsService这个接口;

  1. package com.ucs.security.context;  
  2.   
  3. import java.util.HashSet;  
  4. import java.util.Set;  
  5.   
  6. import javax.annotation.Resource;  
  7.   
  8. import org.springframework.security.core.GrantedAuthority;  
  9. import org.springframework.security.core.authority.GrantedAuthorityImpl;  
  10. import org.springframework.security.core.userdetails.User;  
  11. import org.springframework.security.core.userdetails.UserDetails;  
  12. import org.springframework.security.core.userdetails.UserDetailsService;  
  13. import org.springframework.security.core.userdetails.UsernameNotFoundException;  
  14. import org.springframework.stereotype.Service;  
  15.   
  16. import com.ucs.security.face.SecurityTestInterface;  
  17. import com.ucs.security.pojo.Users;  
  18. /** 
  19.  * 在spring-security.xml中如果配置了 
  20.  * <authentication-manager> 
  21.         <authentication-provider user-service-ref="myUserDetailsService" /> 
  22.   </authentication-manager> 
  23.  * 将会使用这个类进行权限的验证。 
  24.  *  
  25.  * **/  
  26. @Service("myUserDetailsService")  
  27. public class MyUserDetailsService implements UserDetailsService{  
  28.     @Resource  
  29.     private SecurityTestInterface dao;  
  30.   
  31.     //登录验证  
  32.     public UserDetails loadUserByUsername(String name)  
  33.             throws UsernameNotFoundException {  
  34.         System.out.println("show login name:"+name+" ");  
  35.         Users users =dao.findbyUsername(name);  
  36.         Set<GrantedAuthority> grantedAuths=obtionGrantedAuthorities(users);  
  37.           
  38.         boolean enables = true;  
  39.         boolean accountNonExpired = true;  
  40.         boolean credentialsNonExpired = true;  
  41.         boolean accountNonLocked = true;  
  42.         //封装成spring security的user  
  43.         User userdetail = new User(users.getName(), users.getPassword(), enables, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuths);  
  44.         return userdetail;  
  45.     }  
  46.     //查找用户权限  
  47.     public Set<GrantedAuthority> obtionGrantedAuthorities(Users users){  
  48.         Set<GrantedAuthority> authSet=new HashSet<GrantedAuthority>();  
  49.         authSet.add(new GrantedAuthorityImpl(users.getRole()));  
  50.         return authSet;  
  51.     }  
  52.   
  53. }  
登录的时候获取登录的用户名,然后通过数据库去查找该用户拥有的权限将权限增加到Set<GrantedAuthority>中,当然可以加多个权限进去,只要用户拥有其中一个权限就可以登录进来。

然后将从数据库查到的密码和权限设置到security自己的User类中,security会自己去匹配前端发来的密码和用户权限去对比,然后判断用户是否可以登录进来。登录失败还是停留在登录界面。

在user.jsp中测试了用户权限来验证是否可以拦截没有权限用户去访问资源:

点击添加报表管理员或者删除报表管理员时候会跳到403.jsp因为没有权限去访问资源,在接口上我们设置了访问的权限:

  1. @RolesAllowed("ROLE_ADMIN")//拥有ROLE_ADMIN权限的用户才可进入此方法  
  2.     boolean addreport_admin();  
  3.     @RolesAllowed("ROLE_ADMIN")  
  4.     boolean deletereport_admin();  

因为登录进来的用户时ROLE_USER权限的。就被拦截下来。logout是登出,返回到登录界面,并且用户在security中的缓存清掉了。一样会对资源进行拦截。


接下来应该是对访问url进行资源权限管理,

先说说上面的那种方法吧,在接口上加上权限,其实这已经差不多可以在一般的项目中可以使用。作为管理员用户他可以有多种角色,那么他可以在访问ROLE_USER,ROLE_ADMIN的东西,而在接口上没有加上访问权限的,最为公共部分,每个人都可以访问的,访问需要验证的资源就会自动跳转到登录界面,登录后访问没有权限的资源就会提示403,没有权限。基本可以满足项目需求。在spring-security.xml中也就不需要配置,可以让所有的地址同行。这种方法是简单,但是不灵活。如果项目是在做了的时候固定什么用户有什么权限,可以访问哪些方法,那么这种配置时比较简单的,但是如果项目需要后期超级管理员可以更改角色可以访问哪些资源的话,那么这种昂发就不行了。需要用将角色可访问资源链接保存到数据库,可以随时更改。

下面就介绍这种可以修改角色访问资源的灵活配置。用哪种方法还是更具项目需求而定。


需要修改配置文件在http标签里添加custom-filter,添加资源权限控制配置,资源权限控制中涉及到两个类。MyAccessDecisionManager和MySecurityMetadataSource

先写上spring-security.xml的配置文件;

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans:beans xmlns="http://www.springframework.org/schema/security"  
  3.   xmlns:beans="http://www.springframework.org/schema/beans"  
  4.   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  5.   xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6.            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7.            http://www.springframework.org/schema/security  
  8.            http://www.springframework.org/schema/security/spring-security-3.1.xsd">  
  9.   
  10.   
  11.     <!-- 启用方法控制访问权限  用于直接拦截接口上的方法,拥有权限才能访问此方法-->  
  12.     <global-method-security jsr250-annotations="enabled"/>  
  13.     <!-- 自己写登录页面,并且登陆页面不拦截 -->  
  14.     <http pattern="/jsp/login.jsp" security="none" />  
  15.       
  16.     <!-- 配置拦截页面  -->                              <!-- 启用页面级权限控制 使用表达式 -->  
  17.     <http auto-config='true' access-denied-page="/jsp/403.jsp" use-expressions="true">  
  18.                                     <!-- requires-channel="any" 设置访问类型http或者https -->  
  19.         <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" requires-channel="any"/>    
  20.         <!-- intercept-url pattern="/admin/**" 拦截地址的设置有加载先后的顺序,  
  21.         admin/**在前面请求admin/admin.jsp会先去拿用户验证是否有ROLE_ADMIN权限,有则通过,没有就拦截。如果shi   
  22.         pattern="/**" 设置在前面,当前登录的用户有ROLE_USER权限,那么就可以登录到admin/admin.jsp  
  23.         所以两个配置有先后的。  
  24.          -->  
  25.         <intercept-url pattern="/**" access="hasRole('ROLE_USER')" requires-channel="any"/>     
  26.           
  27.         <!-- 设置用户默认登录页面 -->  
  28.         <form-login login-page="/jsp/login.jsp"/>  
  29.         <!-- 基于url的权限控制,加载权限资源管理拦截器,如果进行这样的设置,那么  
  30.          <intercept-url pattern="/admin/**" 就可以不进行配置了,会在数据库的资源权限中得到对应。  
  31.          对于没有找到资源的权限为null的值就不需要登录才可以查看,相当于public的。可以公共访问  
  32.           -->  
  33.         <custom-filter ref="securityFilter" before="FILTER_SECURITY_INTERCEPTOR"/>  
  34.     </http>  
  35.       
  36.     <!-- 当基于方法权限控制的时候只需要此配置,在接口上加上权限即可控制方法的调用  
  37.     <authentication-manager>  
  38.         <authentication-provider user-service-ref="myUserDetailsService"/>  
  39.     </authentication-manager>  
  40.       -->  
  41.       
  42.       
  43.     <!-- 资源权限控制 -->  
  44.     <beans:bean id="securityFilter" class="com.ucs.security.context.MySecurityFilter">  
  45.         <!-- 用户拥有的权限 -->  
  46.         <beans:property name="authenticationManager" ref="myAuthenticationManager" />  
  47.         <!-- 用户是否拥有所请求资源的权限 -->  
  48.         <beans:property name="accessDecisionManager" ref="myAccessDecisionManager" />  
  49.         <!-- 资源与权限对应关系 -->  
  50.         <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource" />  
  51.     </beans:bean>  
  52.       
  53.       
  54.     <authentication-manager alias="myAuthenticationManager">  
  55.         <!-- 权限控制 引用 id是myUserDetailsService的server -->  
  56.         <authentication-provider user-service-ref="myUserDetailsService"/>  
  57.     </authentication-manager>  
  58. </beans:beans>              

在http标签中添加了:<custom-filter ref="securityFilter" before="FILTER_SECURITY_INTERCEPTOR"/>,用于地址的拦截

MySecurityFilter这个类是拦截中一个主要的类,拦截的时候会先通过这里:

  1. package com.ucs.security.context;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import javax.annotation.Resource;  
  6. import javax.servlet.Filter;  
  7. import javax.servlet.FilterChain;  
  8. import javax.servlet.FilterConfig;  
  9. import javax.servlet.ServletException;  
  10. import javax.servlet.ServletRequest;  
  11. import javax.servlet.ServletResponse;  
  12.   
  13. import org.apache.log4j.Logger;  
  14. import org.springframework.security.access.SecurityMetadataSource;  
  15. import org.springframework.security.access.intercept.AbstractSecurityInterceptor;  
  16. import org.springframework.security.access.intercept.InterceptorStatusToken;  
  17. import org.springframework.security.web.FilterInvocation;  
  18. import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;  
  19.   
  20.   
  21.   
  22. public class MySecurityFilter extends AbstractSecurityInterceptor implements Filter {  
  23.     Logger log=Logger.getLogger(MySecurityFilter.class);  
  24.       
  25.     private FilterInvocationSecurityMetadataSource securityMetadataSource;  
  26.     public SecurityMetadataSource obtainSecurityMetadataSource() {  
  27.         return this.securityMetadataSource;  
  28.     }  
  29.     public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {  
  30.         return securityMetadataSource;  
  31.     }  
  32.   
  33.     public void setSecurityMetadataSource(  
  34.             FilterInvocationSecurityMetadataSource securityMetadataSource) {  
  35.         this.securityMetadataSource = securityMetadataSource;  
  36.     }  
  37.   
  38.     @Override  
  39.     public void destroy() {  
  40.         // TODO Auto-generated method stub  
  41.           
  42.     }  
  43.   
  44.     @Override  
  45.     public void doFilter(ServletRequest req, ServletResponse res,  
  46.             FilterChain chain) throws IOException, ServletException {  
  47.         FilterInvocation fi=new FilterInvocation(req,res,chain);  
  48.         log.info("--------MySecurityFilter--------");  
  49.         invok(fi);  
  50.     }  
  51.   
  52.       
  53.   
  54.     private void invok(FilterInvocation fi) throws IOException, ServletException {  
  55.         // object为FilterInvocation对象  
  56.         //1.获取请求资源的权限  
  57.         //执行Collection<ConfigAttribute> attributes = SecurityMetadataSource.getAttributes(object);  
  58.         //2.是否拥有权限  
  59.         //获取安全主体,可以强制转换为UserDetails的实例  
  60.         //1) UserDetails  
  61.         // Authentication authenticated = authenticateIfRequired();  
  62.         //this.accessDecisionManager.decide(authenticated, object, attributes);  
  63.         //用户拥有的权限  
  64.         //2) GrantedAuthority  
  65.         //Collection<GrantedAuthority> authenticated.getAuthorities()  
  66.         log.info("用户发送请求! ");  
  67.         InterceptorStatusToken token = null;  
  68.           
  69.         token = super.beforeInvocation(fi);  
  70.           
  71.         try {  
  72.             fi.getChain().doFilter(fi.getRequest(), fi.getResponse());  
  73.         } finally {  
  74.             super.afterInvocation(token, null);  
  75.         }  
  76.     }  
  77.   
  78.     @Override  
  79.     public void init(FilterConfig arg0) throws ServletException {  
  80.         // TODO Auto-generated method stub  
  81.           
  82.     }  
  83.   
  84.       
  85.     public Class<? extends Object> getSecureObjectClass() {  
  86.         //下面的MyAccessDecisionManager的supports方面必须放回true,否则会提醒类型错误  
  87.         return FilterInvocation.class;  
  88.     }  
  89.       
  90.   
  91. }  

MySecurityMetadataSource这个类是查找和匹配所有角色的资源对应关系的,通过访问一个资源,可以得到这个资源的角色,返货给下一个类MyAccessDecisionManager去判断是够可以放行通过验证。

  1. package com.ucs.security.context;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Collection;  
  5. import java.util.HashMap;  
  6. import java.util.Iterator;  
  7. import java.util.List;  
  8. import java.util.Map;  
  9. import java.util.Set;  
  10. import java.util.Map.Entry;  
  11.   
  12. import javax.annotation.Resource;  
  13.   
  14. import org.apache.log4j.Logger;  
  15. import org.springframework.security.access.ConfigAttribute;  
  16. import org.springframework.security.access.SecurityConfig;  
  17. import org.springframework.security.web.FilterInvocation;  
  18. import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;  
  19. import org.springframework.stereotype.Service;  
  20.   
  21. import com.google.gson.Gson;  
  22. import com.ucs.security.face.SecurityTestInterface;  
  23. import com.ucs.security.pojo.URLResource;  
  24.   
  25.   
  26.   
  27. @Service("mySecurityMetadataSource")  
  28. public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource{  
  29.     //由spring调用  
  30.     Logger log=Logger.getLogger(MySecurityMetadataSource.class);  
  31.     @Resource  
  32.     private SecurityTestInterface dao;  
  33.     private static Map<String, Collection<ConfigAttribute>> resourceMap = null;  
  34.   
  35.     /*public MySecurityMetadataSource() { 
  36.          
  37.         loadResourceDefine(); 
  38.     }*/  
  39.       
  40.   
  41.     public Collection<ConfigAttribute> getAllConfigAttributes() {  
  42.         // TODO Auto-generated method stub  
  43.         return null;  
  44.     }  
  45.   
  46.     public boolean supports(Class<?> clazz) {  
  47.         // TODO Auto-generated method stub  
  48.         return true;  
  49.     }  
  50.     //加载所有资源与权限的关系  
  51.     private void loadResourceDefine() {  
  52.         if(resourceMap == null) {  
  53.             resourceMap = new HashMap<String, Collection<ConfigAttribute>>();  
  54.             /*List<String> resources ; 
  55.             resources = Lists.newArrayList("/jsp/user.do","/jsp/getoutput.do");*/  
  56.             List<URLResource> findResources = dao.findResource();  
  57.               
  58.             for(URLResource url_resource:findResources){  
  59.                 Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();  
  60.                 ConfigAttribute configAttribute = new SecurityConfig(url_resource.getRole_Name());  
  61.                 for(String resource:url_resource.getRole_url()){  
  62.                     configAttributes.add(configAttribute);  
  63.                     resourceMap.put(resource, configAttributes);  
  64.                 }  
  65.                   
  66.             }  
  67.             //以权限名封装为Spring的security Object  
  68.               
  69.         }  
  70.         Gson gson =new Gson();  
  71.         log.info("权限资源对应关系:"+gson.toJson(resourceMap));  
  72.           
  73.           
  74.         Set<Entry<String, Collection<ConfigAttribute>>> resourceSet = resourceMap.entrySet();  
  75.         Iterator<Entry<String, Collection<ConfigAttribute>>> iterator = resourceSet.iterator();  
  76.           
  77.     }  
  78.     //返回所请求资源所需要的权限  
  79.     public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {  
  80.           
  81.         String requestUrl = ((FilterInvocation) object).getRequestUrl();  
  82.         log.info("requestUrl is " + requestUrl);  
  83.         if(resourceMap == null) {  
  84.             loadResourceDefine();  
  85.         }  
  86.         log.info("通过资源定位到的权限:"+resourceMap.get(requestUrl));  
  87.         return resourceMap.get(requestUrl);  
  88.     }  
  89.   
  90. }  

MyAccessDecisionManager这个类,就是获取到请求资源的角色后判断用户是否有这个权限可以访问这个资源。如果获取到的角色是null,那就放行通过,这主要是对于那些不需要验证的公共可以访问的方法。就不需要权限了。可以直接访问。

  1. import java.util.Collection;  
  2. import java.util.Iterator;  
  3. import org.apache.log4j.Logger;  
  4. import org.springframework.security.access.AccessDecisionManager;  
  5. import org.springframework.security.access.AccessDeniedException;  
  6. import org.springframework.security.access.ConfigAttribute;  
  7. import org.springframework.security.authentication.InsufficientAuthenticationException;  
  8. import org.springframework.security.core.Authentication;  
  9. import org.springframework.security.core.GrantedAuthority;  
  10. import org.springframework.stereotype.Service;  
  11. @Service("myAccessDecisionManager")  
  12. public class MyAccessDecisionManager implements AccessDecisionManager{  
  13.     Logger log=Logger.getLogger(MyAccessDecisionManager.class);  
  14.     @Override  
  15.     public void decide(Authentication authentication, Object object,  
  16.             Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,  
  17.             InsufficientAuthenticationException {  
  18.             // TODO Auto-generated method stub  
  19.         //如果对应资源没有找到角色 则放行  
  20.             if(configAttributes == null){  
  21.                   
  22.                 return ;  
  23.             }  
  24.           
  25.             log.info("object is a URL:"+object.toString());  //object is a URL.  
  26.             Iterator<ConfigAttribute> ite=configAttributes.iterator();  
  27.             while(ite.hasNext()){  
  28.                 ConfigAttribute ca=ite.next();  
  29.                 String needRole=ca.getAttribute();  
  30.                 for(GrantedAuthority ga:authentication.getAuthorities()){  
  31.                     if(needRole.equals(ga.getAuthority())){  //ga is user's role.  
  32.                         return;  
  33.                     }  
  34.                 }  
  35.             }  
  36.             throw new AccessDeniedException("no right");  
  37.               
  38.           
  39.   
  40.           
  41.     }  
  42.   
  43.     @Override  
  44.     public boolean supports(ConfigAttribute arg0) {  
  45.         // TODO Auto-generated method stub  
  46.         return true;  
  47.     }  
  48.   
  49.     @Override  
  50.     public boolean supports(Class<?> arg0) {  
  51.         // TODO Auto-generated method stub  
  52.         return true;  
  53.     }  
  54.   
  55. }  

dao的数据库的查找的代码就补贴出来了。

security的权限控制小总结

首先用户没有登录的时候可以访问一些公共的资源,但是必须把<intercept-url pattern="/**" access="hasRole('ROLE_USER')" requires-channel="any"/>  配置删掉,不拦截,任何资源都可以访问,这样公共资源可以任意访问。

对于需要权限的资源已经在数据库配置,如果去访问会直接跳到登录界面。需要登录。

根据登录用户不同分配到的角色就不一样,根据角色不同来获取该角色可以访问的资源。

拥有ROLE_USER角色用户去访问ROLE_ADMIN的资源会返回到403.jsp页面。拥有ROLE_USER和ROLE_ADMIN角色的用户可以去访问两种角色的资源。公共的资源两种角色都可以访问。

security提供了默认的登出地址。登出后用户在spring中的缓存就清除了。

之前做过基于spring-AOP的url和角色的控制,那时候觉得还不错,能做一些简单的权限控制。学习完security发现security真的是很专业啊。能做复杂的权限控制,还只支持页面上的权限控制。





本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Spring Security3的使用方法有4种
使用Spring Security实现权限管理
Spring Security笔记:使用数据库进行用户认证(form login using database)
spring boot-admin 监控中心 配置登录密码
SpringSide 3 中的安全框架
Dubbo与Zookeeper、SpringMVC整合和使用(负载均衡、容错)配置详解,dubbo-admin 下载
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服