- <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">廢話不多說,直接進入主題。此文章會慢慢跟新。我就在此做一個記錄。以后方便自己查閱。</span>
spring security3.1的文檔可以去官網下,里面有jar包和說明文檔
關于Spring Security3.1的配置網上也很多,但是看看是沒有用的,還是需要自己慢慢研究搭環(huán)境。
先從最簡單的開始說起。
測試項目在:http://download.csdn.net/detail/cctv99999999/7112505 可以下載,測試通過可用。數(shù)據庫采用mysql。需要的同學可以去看一下。jar包已經配好。
環(huán)境搭建
首先我們先把環(huán)境搭起來,
我用的是spring mvc+spring securiity3.1,先要配置web.xml文件,
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app version="3.0"
- xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
- http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
-
-
-
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>/WEB-INF/security/*.xml</param-value>
- </context-param>
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
-
- <servlet>
- <servlet-name>dispatcher</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>/WEB-INF/security/dispatcher-servlet.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>dispatcher</servlet-name>
- <url-pattern>*.do</url-pattern>
- </servlet-mapping>
- <!-- 權限 -->
- <filter>
- <filter-name>springSecurityFilterChain</filter-name>
- <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
- </filter>
-
- <filter-mapping>
- <filter-name>springSecurityFilterChain</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
-
- </web-app>
spring-security.xml是用來配置security的,dispatcher-servlet.xml是用來配置spring mvc的,
spring mvc的配置很簡單,我上面都打了注釋。
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:mvc="http://www.springframework.org/schema/mvc"
- xmlns:p="http://www.springframework.org/schema/p"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.0.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
- http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
- <!--
- 使Spring支持自動檢測組件,如注解的Controller
- -->
-
- <context:component-scan base-package="com"/>
- <aop:aspectj-autoproxy/> <!-- 開啟AOP -->
-
-
-
- <bean id="viewResolver"
- class="org.springframework.web.servlet.view.InternalResourceViewResolver"
- p:prefix="/jsp/"
- p:suffix=".jsp"
- />
-
-
-
- <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
- <property name="messageConverters">
- <list >
- <ref bean="mappingJacksonHttpMessageConverter" />
- </list>
- </property>
- </bean>
-
-
- <bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
- <!-- 數(shù)據庫連接配置 -->
- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
- <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
- <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/power"></property>
- <property name="user" value="root"></property>
- <property name="password" value="516725"></property>
- <property name="minPoolSize" value="10"></property>
- <property name="MaxPoolSize" value="50"></property>
- <property name="MaxIdleTime" value="60"></property><!-- 最少空閑連接 -->
- <property name="acquireIncrement" value="5"></property><!-- 當連接池中的連接耗盡的時候 c3p0一次同時獲取的連接數(shù)。 -->
- <property name="TestConnectionOnCheckout" value="true" ></property>
- </bean>
- <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
- <property name="dataSource">
- <ref local="dataSource"/>
- </property>
- </bean>
- <!-- 事務申明 -->
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" >
- <ref local="dataSource"/>
- </property>
- </bean>
- <!-- Aop切入點 -->
- <aop:config>
- <aop:pointcut expression="within(com.ucs.security.dao.*)" id="serviceOperaton"/>
- <aop:advisor advice-ref="txadvice" pointcut-ref="serviceOperaton"/>
- </aop:config>
- <tx:advice id="txadvice" transaction-manager="transactionManager">
- <tx:attributes>
- <tx:method name="delete*" propagation="REQUIRED"/>
- </tx:attributes>
- </tx:advice>
- </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中,如下
- <?xml version="1.0" encoding="UTF-8"?>
- <beans:beans xmlns="http://www.springframework.org/schema/security"
- xmlns:beans="http://www.springframework.org/schema/beans"
- 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.1.xsd">
-
- </beans:beans>
這樣環(huán)境基本就搭建好了。
硬編碼的簡單配置
在春天spring-securoty.xml配置文件中添加如下配置:
- <!-- 對所有頁面進行攔截,需要ROLE_USER權限-->
- <http auto-config='true'>
- <intercept-url pattern="/**" access="ROLE_USER" />
- </http>
- <!-- 權限配置 jimi擁有兩種權限 bob擁有一種權限-->
- <authentication-manager>
- <authentication-provider>
- <user-service>
- <user name="jimi" password="123" authorities="ROLE_USER, ROLE_ADMIN" />
- <user name="bob" password="456" authorities="ROLE_USER" />
- </user-service>
- </authentication-provider>
- </authentication-manager>
然后基本就可以啟動了。security會自動生成登錄頁面的。http://localhost:8080/spring-security/spring_security_login,這是自動生成的網頁代碼
他有默認的請求路徑和post字段,如果我們自己寫登錄頁面這些都是不能改的。
第二種是通過查數(shù)據庫來找到用戶擁有的權限來控制用戶的訪問:
建表sql 數(shù)據庫采用mysql
- CREATE TABLE `user` (
-
- `Id` int(11) NOT NULL auto_increment,
-
- `logname` varchar(255) default NULL,
-
- `password` varchar(255) default NULL,
-
- `role_ids` varchar(255) default NULL,
-
- PRIMARY KEY (`Id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
修改spring-security.xml文件:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans:beans xmlns="http://www.springframework.org/schema/security"
- xmlns:beans="http://www.springframework.org/schema/beans"
- 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.1.xsd">
-
- <!-- 啟用方法控制訪問權限 用于直接攔截接口上的方法,擁有權限才能訪問此方法-->
- <global-method-security jsr250-annotations="enabled"/>
- <!-- 自己寫登錄頁面,并且登陸頁面不攔截 -->
- <http pattern="/jsp/login.jsp" security="none" />
-
- <!-- 配置攔截頁面 --> <!-- 啟用頁面級權限控制 使用表達式 -->
- <http auto-config='true' access-denied-page="/jsp/403.jsp" use-expressions="true">
- <intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
- <!-- 設置用戶默認登錄頁面 -->
- <form-login login-page="/jsp/login.jsp"/>
- </http>
-
- <authentication-manager>
- <!-- 權限控制 引用 id是myUserDetailsService的server -->
- <authentication-provider user-service-ref="myUserDetailsService"/>
- </authentication-manager>
-
- </beans:beans>
首先編輯自己的登錄頁面:登錄界面中提交的字段必須是security默認的,
- <%@ page language="java" contentType="text/html; charset=utf-8"
- pageEncoding="utf-8"%>
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <title>登錄界面</title>
- </head>
- <body>
- <h3>登錄界面</h3>
-
- <form action="/spring-security/j_spring_security_check" method="post">
- <table>
- <tr><td>User:</td><td><input type='text' name='j_username' value=''></td></tr>
- <tr><td>Password:</td><td><input type='password' name='j_password'/></td></tr>
- <tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
- </table>
- </form>
-
- </body>
- </html>
user.jsp頁面:user頁面是ROLE_USER權限界面,其中引用了security標簽,在配置文件中 use-expressions="true"代表啟用頁面控制語言,就是根據不同的權限頁面顯示該權限應該顯示的內容。如果查看網頁源代碼也是看不到初自己權限以外的內容的。
- <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
- <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
- <%@ taglib prefix="s" uri="http://www.springframework.org/tags/form" %>
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; utf-8">
- <title>Insert title here</title>
- </head>
- <body>
- <h5><a href="../j_spring_security_logout">logout</a></h5>
- <!-- 擁有ROLE_ADMIN權限的才看的到 -->
- <sec:authorize access="hasRole('ROLE_ADMIN')">
- <form action="#">
- 賬號:<input type="text" /><br/>
- 密碼:<input type="password"/><br/>
- <input type="submit" value="submit"/>
- </form>
- </sec:authorize>
-
- <p/>
- <sec:authorize access="hasRole('ROLE_USER')">
- 顯示擁有ROLE_USER權限的頁面<br/>
- <form action="#">
- 賬號:<input type="text" /><br/>
- 密碼:<input type="password"/><br/>
- <input type="submit" value="submit"/>
- </form>
-
- </sec:authorize>
- <p/>
- <h5>測試方法控制訪問權限</h5>
- <a href="addreport_admin.do">添加報表管理員</a><br/>
- <a href="deletereport_admin.do">刪除報表管理員</a>
- </body>
- </html>
Controller層
- package com.ucs.security.server;
-
-
- import javax.annotation.Resource;
- import javax.servlet.http.HttpServletRequest;
-
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.ResponseBody;
- import org.springframework.web.servlet.ModelAndView;
-
- import com.ucs.security.face.SecurityTestInterface;
-
- @Controller
- public class SecurityTest {
- @Resource
- private SecurityTestInterface dao;
-
- @RequestMapping(value="/jsp/getinput")//查看最近收入
- @ResponseBody
- public boolean getinput(HttpServletRequest req,HttpServletRequest res){
- boolean b=dao.getinput();
- return b;
- }
-
-
- @RequestMapping(value="/jsp/geoutput")//查看最近支出
- @ResponseBody
- public boolean geoutput(HttpServletRequest req,HttpServletRequest res){
- boolean b=dao.geoutput();
- return b;
- }
-
- @RequestMapping(value="/jsp/addreport_admin")//添加報表管理員
- @ResponseBody
- public boolean addreport_admin(HttpServletRequest req,HttpServletRequest res){
- boolean b=dao.addreport_admin();
- return b;
- }
-
- @RequestMapping(value="/jsp/deletereport_admin")//刪除報表管理員
- @ResponseBody
- public boolean deletereport_admin(HttpServletRequest req,HttpServletRequest res){
- boolean b=dao.deletereport_admin();
- return b;
- }
-
- @RequestMapping(value="/jsp/user")//普通用戶登錄
- public ModelAndView user_login(HttpServletRequest req,HttpServletRequest res){
- dao.user_login();
- return new ModelAndView("user");
- }
-
- }
接口: 在配置文件中<global-method-security jsr250-annotations="enabled"/>就是在接口上設置權限,當擁有權限時才能調用方法,沒有權限是不能調用方法的,保證了安全性
- package com.ucs.security.face;
-
- import javax.annotation.security.RolesAllowed;
-
- import com.ucs.security.pojo.Users;
-
- public interface SecurityTestInterface {
-
- boolean getinput();
-
- boolean geoutput();
- @RolesAllowed("ROLE_ADMIN")//擁有ROLE_ADMIN權限的用戶才可進入此方法
- boolean addreport_admin();
- @RolesAllowed("ROLE_ADMIN")
- boolean deletereport_admin();
-
- Users findbyUsername(String name);
- @RolesAllowed("ROLE_USER")
- void user_login();
-
- }
實現(xiàn)類 dao
- package com.ucs.security.dao;
- import java.sql.SQLException;
- import javax.annotation.Resource;
- import org.apache.log4j.Logger;
- import org.springframework.jdbc.core.JdbcTemplate;
- import org.springframework.jdbc.core.RowCallbackHandler;
- import org.springframework.stereotype.Repository;
- import com.ucs.security.face.SecurityTestInterface;
- import com.ucs.security.pojo.Users;
-
- @Repository("SecurityTestDao")
- public class SecurityTestDao implements SecurityTestInterface{
- Logger log=Logger.getLogger(SecurityTestDao.class);
-
- @Resource
- private JdbcTemplate jdbcTamplate;
- public boolean getinput() {
- log.info("getinput");
- return true;
- }
-
- public boolean geoutput() {
- log.info("geoutput");
- return true;
- }
-
- public boolean addreport_admin() {
- log.info("addreport_admin");
- return true;
- }
-
- public boolean deletereport_admin() {
- log.info("deletereport_admin");
- return true;
- }
-
-
- public Users findbyUsername(String name) {
- final Users users = new Users();
- jdbcTamplate.query("SELECT * FROM USER WHERE logname = ?",
- new Object[] {name},
- new RowCallbackHandler() {
- @Override
- public void processRow(java.sql.ResultSet rs)
- throws SQLException {
- users.setName(rs.getString("logname"));
- users.setPassword(rs.getString("password"));
- users.setRole(rs.getString("role_ids"));
- }
- });
- log.info(users.getName()+" "+users.getPassword()+" "+users.getRole());
- return users;
- }
-
-
-
- @Override
- public void user_login() {
- log.info("擁有ROLE_USER權限的方法訪問:user_login");
-
- }
-
- }
下面就是重要的一個類:MyUserDetailsService這個類需要去實現(xiàn)UserDetailsService這個接口;
- package com.ucs.security.context;
-
- import java.util.HashSet;
- import java.util.Set;
-
- import javax.annotation.Resource;
-
- import org.springframework.security.core.GrantedAuthority;
- import org.springframework.security.core.authority.GrantedAuthorityImpl;
- import org.springframework.security.core.userdetails.User;
- import org.springframework.security.core.userdetails.UserDetails;
- import org.springframework.security.core.userdetails.UserDetailsService;
- import org.springframework.security.core.userdetails.UsernameNotFoundException;
- import org.springframework.stereotype.Service;
-
- import com.ucs.security.face.SecurityTestInterface;
- import com.ucs.security.pojo.Users;
- /**
- * 在spring-security.xml中如果配置了
- * <authentication-manager>
- <authentication-provider user-service-ref="myUserDetailsService" />
- </authentication-manager>
- * 將會使用這個類進行權限的驗證。
- *
- * **/
- @Service("myUserDetailsService")
- public class MyUserDetailsService implements UserDetailsService{
- @Resource
- private SecurityTestInterface dao;
-
- //登錄驗證
- public UserDetails loadUserByUsername(String name)
- throws UsernameNotFoundException {
- System.out.println("show login name:"+name+" ");
- Users users =dao.findbyUsername(name);
- Set<GrantedAuthority> grantedAuths=obtionGrantedAuthorities(users);
-
- boolean enables = true;
- boolean accountNonExpired = true;
- boolean credentialsNonExpired = true;
- boolean accountNonLocked = true;
- //封裝成spring security的user
- User userdetail = new User(users.getName(), users.getPassword(), enables, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuths);
- return userdetail;
- }
- //查找用戶權限
- public Set<GrantedAuthority> obtionGrantedAuthorities(Users users){
- Set<GrantedAuthority> authSet=new HashSet<GrantedAuthority>();
- authSet.add(new GrantedAuthorityImpl(users.getRole()));
- return authSet;
- }
-
- }
登錄的時候獲取登錄的用戶名,然后通過數(shù)據庫去查找該用戶擁有的權限將權限增加到Set<GrantedAuthority>中,當然可以加多個權限進去,只要用戶擁有其中一個權限就可以登錄進來。
然后將從數(shù)據庫查到的密碼和權限設置到security自己的User類中,security會自己去匹配前端發(fā)來的密碼和用戶權限去對比,然后判斷用戶是否可以登錄進來。登錄失敗還是停留在登錄界面。
在user.jsp中測試了用戶權限來驗證是否可以攔截沒有權限用戶去訪問資源:
點擊添加報表管理員或者刪除報表管理員時候會跳到403.jsp因為沒有權限去訪問資源,在接口上我們設置了訪問的權限:
- @RolesAllowed("ROLE_ADMIN")//擁有ROLE_ADMIN權限的用戶才可進入此方法
- boolean addreport_admin();
- @RolesAllowed("ROLE_ADMIN")
- boolean deletereport_admin();
因為登錄進來的用戶時ROLE_USER權限的。就被攔截下來。logout是登出,返回到登錄界面,并且用戶在security中的緩存清掉了。一樣會對資源進行攔截。
接下來應該是對訪問url進行資源權限管理,
先說說上面的那種方法吧,在接口上加上權限,其實這已經差不多可以在一般的項目中可以使用。作為管理員用戶他可以有多種角色,那么他可以在訪問ROLE_USER,ROLE_ADMIN的東西,而在接口上沒有加上訪問權限的,最為公共部分,每個人都可以訪問的,訪問需要驗證的資源就會自動跳轉到登錄界面,登錄后訪問沒有權限的資源就會提示403,沒有權限?;究梢詽M足項目需求。在spring-security.xml中也就不需要配置,可以讓所有的地址同行。這種方法是簡單,但是不靈活。如果項目是在做了的時候固定什么用戶有什么權限,可以訪問哪些方法,那么這種配置時比較簡單的,但是如果項目需要后期超級管理員可以更改角色可以訪問哪些資源的話,那么這種昂發(fā)就不行了。需要用將角色可訪問資源鏈接保存到數(shù)據庫,可以隨時更改。
下面就介紹這種可以修改角色訪問資源的靈活配置。用哪種方法還是更具項目需求而定。
需要修改配置文件在http標簽里添加custom-filter,添加資源權限控制配置,資源權限控制中涉及到兩個類。MyAccessDecisionManager和MySecurityMetadataSource
先寫上spring-security.xml的配置文件;
- <?xml version="1.0" encoding="UTF-8"?>
- <beans:beans xmlns="http://www.springframework.org/schema/security"
- xmlns:beans="http://www.springframework.org/schema/beans"
- 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.1.xsd">
-
-
- <!-- 啟用方法控制訪問權限 用于直接攔截接口上的方法,擁有權限才能訪問此方法-->
- <global-method-security jsr250-annotations="enabled"/>
- <!-- 自己寫登錄頁面,并且登陸頁面不攔截 -->
- <http pattern="/jsp/login.jsp" security="none" />
-
- <!-- 配置攔截頁面 --> <!-- 啟用頁面級權限控制 使用表達式 -->
- <http auto-config='true' access-denied-page="/jsp/403.jsp" use-expressions="true">
- <!-- requires-channel="any" 設置訪問類型http或者https -->
- <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" requires-channel="any"/>
- <!-- intercept-url pattern="/admin/**" 攔截地址的設置有加載先后的順序,
- admin/**在前面請求admin/admin.jsp會先去拿用戶驗證是否有ROLE_ADMIN權限,有則通過,沒有就攔截。如果shi
- pattern="/**" 設置在前面,當前登錄的用戶有ROLE_USER權限,那么就可以登錄到admin/admin.jsp
- 所以兩個配置有先后的。
- -->
- <intercept-url pattern="/**" access="hasRole('ROLE_USER')" requires-channel="any"/>
-
- <!-- 設置用戶默認登錄頁面 -->
- <form-login login-page="/jsp/login.jsp"/>
- <!-- 基于url的權限控制,加載權限資源管理攔截器,如果進行這樣的設置,那么
- <intercept-url pattern="/admin/**" 就可以不進行配置了,會在數(shù)據庫的資源權限中得到對應。
- 對于沒有找到資源的權限為null的值就不需要登錄才可以查看,相當于public的。可以公共訪問
- -->
- <custom-filter ref="securityFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
- </http>
-
- <!-- 當基于方法權限控制的時候只需要此配置,在接口上加上權限即可控制方法的調用
- <authentication-manager>
- <authentication-provider user-service-ref="myUserDetailsService"/>
- </authentication-manager>
- -->
-
-
- <!-- 資源權限控制 -->
- <beans:bean id="securityFilter" class="com.ucs.security.context.MySecurityFilter">
- <!-- 用戶擁有的權限 -->
- <beans:property name="authenticationManager" ref="myAuthenticationManager" />
- <!-- 用戶是否擁有所請求資源的權限 -->
- <beans:property name="accessDecisionManager" ref="myAccessDecisionManager" />
- <!-- 資源與權限對應關系 -->
- <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource" />
- </beans:bean>
-
-
- <authentication-manager alias="myAuthenticationManager">
- <!-- 權限控制 引用 id是myUserDetailsService的server -->
- <authentication-provider user-service-ref="myUserDetailsService"/>
- </authentication-manager>
- </beans:beans>
在http標簽中添加了:<custom-filter ref="securityFilter" before="FILTER_SECURITY_INTERCEPTOR"/>,用于地址的攔截
MySecurityFilter這個類是攔截中一個主要的類,攔截的時候會先通過這里:
- package com.ucs.security.context;
-
- import java.io.IOException;
-
- import javax.annotation.Resource;
- import javax.servlet.Filter;
- import javax.servlet.FilterChain;
- import javax.servlet.FilterConfig;
- import javax.servlet.ServletException;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
-
- import org.apache.log4j.Logger;
- import org.springframework.security.access.SecurityMetadataSource;
- import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
- import org.springframework.security.access.intercept.InterceptorStatusToken;
- import org.springframework.security.web.FilterInvocation;
- import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
-
-
-
- public class MySecurityFilter extends AbstractSecurityInterceptor implements Filter {
- Logger log=Logger.getLogger(MySecurityFilter.class);
-
- private FilterInvocationSecurityMetadataSource securityMetadataSource;
- public SecurityMetadataSource obtainSecurityMetadataSource() {
- return this.securityMetadataSource;
- }
- public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
- return securityMetadataSource;
- }
-
- public void setSecurityMetadataSource(
- FilterInvocationSecurityMetadataSource securityMetadataSource) {
- this.securityMetadataSource = securityMetadataSource;
- }
-
- @Override
- public void destroy() {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void doFilter(ServletRequest req, ServletResponse res,
- FilterChain chain) throws IOException, ServletException {
- FilterInvocation fi=new FilterInvocation(req,res,chain);
- log.info("--------MySecurityFilter--------");
- invok(fi);
- }
-
-
-
- private void invok(FilterInvocation fi) throws IOException, ServletException {
- // object為FilterInvocation對象
- //1.獲取請求資源的權限
- //執(zhí)行Collection<ConfigAttribute> attributes = SecurityMetadataSource.getAttributes(object);
- //2.是否擁有權限
- //獲取安全主體,可以強制轉換為UserDetails的實例
- //1) UserDetails
- // Authentication authenticated = authenticateIfRequired();
- //this.accessDecisionManager.decide(authenticated, object, attributes);
- //用戶擁有的權限
- //2) GrantedAuthority
- //Collection<GrantedAuthority> authenticated.getAuthorities()
- log.info("用戶發(fā)送請求! ");
- InterceptorStatusToken token = null;
-
- token = super.beforeInvocation(fi);
-
- try {
- fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
- } finally {
- super.afterInvocation(token, null);
- }
- }
-
- @Override
- public void init(FilterConfig arg0) throws ServletException {
- // TODO Auto-generated method stub
-
- }
-
-
- public Class<? extends Object> getSecureObjectClass() {
- //下面的MyAccessDecisionManager的supports方面必須放回true,否則會提醒類型錯誤
- return FilterInvocation.class;
- }
-
-
- }
MySecurityMetadataSource這個類是查找和匹配所有角色的資源對應關系的,通過訪問一個資源,可以得到這個資源的角色,返貨給下一個類MyAccessDecisionManager去判斷是夠可以放行通過驗證。
- package com.ucs.security.context;
-
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- import java.util.Map.Entry;
-
- import javax.annotation.Resource;
-
- import org.apache.log4j.Logger;
- import org.springframework.security.access.ConfigAttribute;
- import org.springframework.security.access.SecurityConfig;
- import org.springframework.security.web.FilterInvocation;
- import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
- import org.springframework.stereotype.Service;
-
- import com.google.gson.Gson;
- import com.ucs.security.face.SecurityTestInterface;
- import com.ucs.security.pojo.URLResource;
-
-
-
- @Service("mySecurityMetadataSource")
- public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource{
- //由spring調用
- Logger log=Logger.getLogger(MySecurityMetadataSource.class);
- @Resource
- private SecurityTestInterface dao;
- private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
-
- /*public MySecurityMetadataSource() {
-
- loadResourceDefine();
- }*/
-
-
- public Collection<ConfigAttribute> getAllConfigAttributes() {
- // TODO Auto-generated method stub
- return null;
- }
-
- public boolean supports(Class<?> clazz) {
- // TODO Auto-generated method stub
- return true;
- }
- //加載所有資源與權限的關系
- private void loadResourceDefine() {
- if(resourceMap == null) {
- resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
- /*List<String> resources ;
- resources = Lists.newArrayList("/jsp/user.do","/jsp/getoutput.do");*/
- List<URLResource> findResources = dao.findResource();
-
- for(URLResource url_resource:findResources){
- Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
- ConfigAttribute configAttribute = new SecurityConfig(url_resource.getRole_Name());
- for(String resource:url_resource.getRole_url()){
- configAttributes.add(configAttribute);
- resourceMap.put(resource, configAttributes);
- }
-
- }
- //以權限名封裝為Spring的security Object
-
- }
- Gson gson =new Gson();
- log.info("權限資源對應關系:"+gson.toJson(resourceMap));
-
-
- Set<Entry<String, Collection<ConfigAttribute>>> resourceSet = resourceMap.entrySet();
- Iterator<Entry<String, Collection<ConfigAttribute>>> iterator = resourceSet.iterator();
-
- }
- //返回所請求資源所需要的權限
- public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
-
- String requestUrl = ((FilterInvocation) object).getRequestUrl();
- log.info("requestUrl is " + requestUrl);
- if(resourceMap == null) {
- loadResourceDefine();
- }
- log.info("通過資源定位到的權限:"+resourceMap.get(requestUrl));
- return resourceMap.get(requestUrl);
- }
-
- }
MyAccessDecisionManager這個類,就是獲取到請求資源的角色后判斷用戶是否有這個權限可以訪問這個資源。如果獲取到的角色是null,那就放行通過,這主要是對于那些不需要驗證的公共可以訪問的方法。就不需要權限了??梢灾苯釉L問。
- import java.util.Collection;
- import java.util.Iterator;
- import org.apache.log4j.Logger;
- import org.springframework.security.access.AccessDecisionManager;
- import org.springframework.security.access.AccessDeniedException;
- import org.springframework.security.access.ConfigAttribute;
- import org.springframework.security.authentication.InsufficientAuthenticationException;
- import org.springframework.security.core.Authentication;
- import org.springframework.security.core.GrantedAuthority;
- import org.springframework.stereotype.Service;
- @Service("myAccessDecisionManager")
- public class MyAccessDecisionManager implements AccessDecisionManager{
- Logger log=Logger.getLogger(MyAccessDecisionManager.class);
- @Override
- public void decide(Authentication authentication, Object object,
- Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,
- InsufficientAuthenticationException {
- // TODO Auto-generated method stub
- //如果對應資源沒有找到角色 則放行
- if(configAttributes == null){
-
- return ;
- }
-
- log.info("object is a URL:"+object.toString()); //object is a URL.
- Iterator<ConfigAttribute> ite=configAttributes.iterator();
- while(ite.hasNext()){
- ConfigAttribute ca=ite.next();
- String needRole=ca.getAttribute();
- for(GrantedAuthority ga:authentication.getAuthorities()){
- if(needRole.equals(ga.getAuthority())){ //ga is user's role.
- return;
- }
- }
- }
- throw new AccessDeniedException("no right");
-
-
-
-
- }
-
- @Override
- public boolean supports(ConfigAttribute arg0) {
- // TODO Auto-generated method stub
- return true;
- }
-
- @Override
- public boolean supports(Class<?> arg0) {
- // TODO Auto-generated method stub
- return true;
- }
-
- }
dao的數(shù)據庫的查找的代碼就補貼出來了。
security的權限控制小總結
首先用戶沒有登錄的時候可以訪問一些公共的資源,但是必須把<intercept-url pattern="/**" access="hasRole('ROLE_USER')" requires-channel="any"/> 配置刪掉,不攔截,任何資源都可以訪問,這樣公共資源可以任意訪問。
對于需要權限的資源已經在數(shù)據庫配置,如果去訪問會直接跳到登錄界面。需要登錄。
根據登錄用戶不同分配到的角色就不一樣,根據角色不同來獲取該角色可以訪問的資源。
擁有ROLE_USER角色用戶去訪問ROLE_ADMIN的資源會返回到403.jsp頁面。擁有ROLE_USER和ROLE_ADMIN角色的用戶可以去訪問兩種角色的資源。公共的資源兩種角色都可以訪問。
security提供了默認的登出地址。登出后用戶在spring中的緩存就清除了。
之前做過基于spring-AOP的url和角色的控制,那時候覺得還不錯,能做一些簡單的權限控制。學習完security發(fā)現(xiàn)security真的是很專業(yè)啊。能做復雜的權限控制,還只支持頁面上的權限控制。