基本的配置步骤就是 web.xml ->applicationContext-security.xml->相关类的编写.
数据库大概: user->role->resource 两两之间都是多对多的关系
1.web.xml配置:
<listener>
<listener-class>com.lowkey.crm.listener.ResourceLoaderListener </listener-class>
</listener>
<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>
<filter>
配置一个监听器,使得我们项目启动的时候,我们就把所有的资源都放置在servletContext中,方便我们提取资源.
关于DelegatingFilterProxy简而言之就是 提供web.xml和applicationContext之间的联系.地下的maaping 配置说明了过滤的范围.
2.我建立了一个新的xml文件 applicationContext-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.0.xsd">
...........好多配置在里面...............
</beans:beans>
——————————————...........好多配置在里面..............——————————————————————————
<authentication-manager alias="authenticationManager">
<!-- 关于权限的业务逻辑,必须实现该接口 -->
<authentication-provider user-service-ref="securityService">
<password-encoder hash="md5" />
</authentication-provider>
</authentication-manager>
注意securityService.这个类 我们是需要创建的.
拦截器配置:
<beans:bean id ="resourceSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="accessDecisionManager" ref="accessDecisionManager"/>
<beans:property name="objectD efinitionSource" ref="resourceFilterInvocationDefinitionSource"/>
<beans:property name="observeOncePerRequest" value="false"></beans:property><!--很坑人,很严格的验证 -->
</beans:bean>
-----------resourceFilterInvocationDefinitionSource-------
<beans:bean id="resourceFilterInvocationDefinitionSource"
class="com.lowkey.crm.security.ResourceFilterInvocationDefinitionSource">
</beans:bean>
------accessDecisionManager--------------
<beans:bean id="accessDecisionManager"
class="org.springframework.security.access.vote.AffirmativeBased">
<beans:property name="allowIfAllAbstainDecisions" value="false"></beans:property>
<beans:property name="decisionVoters">
<beans:list> <!-- 验证用户是否role开头 -->
<beans:bean class="org.springframework.security.access.vote.RoleVoter"></beans:bean>
<!-- 这里将用url获得的角色列表,与当前用户角色列表匹配 -->
<beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter"></beans:bean>
</beans:list>
</beans:property>
</beans:bean>
--------403.jsp是我们需要跳转的错误页面并且login.jsp是不需要验证的--------
<http access-denied-page="/403.jsp" auto-config="true" create-session="always">
<!-- 不用验证的路径 -->
<intercept-url pattern="/login.jsp" filters="none" />
<form-login login-page="/login.jsp" default-target-url="/index.action"
authentication-failure-url="/login.jsp?error=1" />
<logout logout-success-url="/login.jsp" />
<http-basic />
<remember-me key="testKey" services-ref="rememberMeServices"></remember-me>
<custom-filter after="FILTER_SECURITY_INTERCEPTOR" ref="resourceSecurityInterceptor"/>
</http>
----------关于rememberme的配置,就是说几天内登录都有效的那种东西------
<beans:bean id="resourceFilterInvocationDefinitionSource"
class="com.lowkey.crm.security.ResourceFilterInvocationDefinitionSource">
</beans:bean>
<beans:bean id="remenberMeProcessingFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager"></beans:property>
<beans:property name="rememberMeServices" ref="rememberMeServices"></beans:property>
</beans:bean>
<beans:bean id ="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<beans:property name="userDetailsService" ref="securityService"></beans:property>
<beans:property name="key" value="testkey"/>
<beans:property name="tokenValiditySeconds" value="43200" /><!-- 5天 -->
</beans:bean>
3.相关类的编写:
监听器:
public class ResourceLoaderListener implements ServletContextListener{
//初始化执行的时候的操作
public void contextInitialized(ServletContextEvent servletContextEvent) {
// TODO Auto-generated method stub
//把数据库所有的Resource数据保存在application中.
ServletContext servleContext = servletContextEvent.getServletContext();
SecurityService securityService=
(SecurityService) WebApplicationContextUtils.getWebApplicationContext(servleContext)
.getBean("securityService");
Map<String,String> urlRoles = securityService.getAllURLResouce();
servleContext.setAttribute("urlRoles", urlRoles);
System.out.println("~~~~~~~~~~~contextInitialized URLROLES:"+urlRoles.toString());
}
//销毁时执行的操作,服务停止时从application中移除所有资源
public void contextDestroyed(ServletContextEvent servletContextEvent) {
// TODO Auto-generated method stub
servletContextEvent.getServletContext().removeAttribute("urlRoles");
}
}
---SecurityService的编写-----------------------------------------------
//注入:通过注解的方式获得一个bean
@Service("securityService")
public class SecurityServiceSupport extends HibernateDaoSupport implements SecurityService,UserDetailsService{
@Autowired
public void init(SessionFactory sessionFactory){
super.setSessionFactory(sessionFactory);
}
@SuppressWarnings("unchecked")
public Map<String, String> getAllURLResouce() {
// TODO Auto-generated method stub
//所有资源 都是url类型
List<Resource> urlResource = this.getHibernateTemplate().find("FROM Resource");
Map<String,String> urls = new HashMap<String, String>();
for(Iterator<Resource> it= urlResource.iterator();it.hasNext();){
Resource resource = it.next();
urls.put(resource.getValue(), resource.getRoleString());//通过url资源,找到所有能访问它的角色.
}
return urls;
}
public UserDetails loadUserByUsername(String name)
throws UsernameNotFoundException, DataAccessException {
System.out.println("~~~~~~~~~~~loadUserByUsername USERNAME:"+name);
List<User> users = null;
try {
users = this.getHibernateTemplate().find(
"FROM User user WHERE user.name=? AND user.disabled=false", name);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(users.size()>0){
System.out.println("~~~~~~~~~~~~~~~~登陆的用户为:"+users.get(0).getName());
return (UserDetails) users.get(0);
}else{
System.out.println("用户:" + name + "没有找到!");
throw new UsernameNotFoundException("用户:" + name + "没有找到!");
}
// TODO Auto-generated method stub
}
}
----------------------ResourceFilterInvocationDefinitionSource的编写 -------------------------
public class ResourceFilterInvocationDefinitionSource
implements FilterInvocationSecurityMetadataSource,InitializingBean{
//在spring 中 有两个类实现这个urlMatcher
//1.AntUrlPathMatcher 2.RegexUrlPathMatcher
private UrlMatcher urlMatcher;
private boolean useAntPath = true;
private boolean setpathlowercase = true;
public Collection getConfigAttributeDefinitions() {
// TODO Auto-generated method stub
return null;
}
public Collection<ConfigAttribute> getAttributes(Object filter)
throws IllegalArgumentException {
FilterInvocation filterInvocation = (FilterInvocation)filter;
ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();
//取得了当前登录用户的所有角色
Map<String ,String > urlRoles = (Map<String, String>) servletContext.getAttribute("urlRoles");
//取得当前用户请求的路径.
String requestURL = filterInvocation.getRequestUrl();
//把用户请求的路径进行授权Role.因为资源表的所有数据都保存在了application里面了
String grantRoleStr = null;
for( Iterator<Map.Entry<String,String>> it = urlRoles.entrySet().iterator();it.hasNext();){
Map.Entry<String,String> entry = it.next();
String url = entry.getKey();
System.out.println("url=============" + url);
if(urlMatcher.pathMatchesUrl(url, requestURL)){
grantRoleStr = entry.getValue();
break;
}
}
// TODO Auto-generated method stub
if(grantRoleStr!=null){
//这个类可以把以逗号分割的一系列字符串,转换为多个ConfigAttibute的ConfigAttributeDefinition对象
ConfigAttributeEditor configAttributeEditor = new ConfigAttributeEditor();
configAttributeEditor.setAsText(grantRoleStr);
return (Collection<ConfigAttribute>) configAttributeEditor.getValue();
}
return null;
}
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet()");
this.urlMatcher = new RegexUrlPathMatcher();
if (useAntPath) {
this.urlMatcher = new AntUrlPathMatcher();
}
System.out.println("this.urlMatcher" + this.urlMatcher);
if ("true".equals(setpathlowercase)) {
if (!this.useAntPath) {
((RegexUrlPathMatcher) this.urlMatcher)
.setRequiresLowerCaseUrl(true);
}
} else if ("false".equals(setpathlowercase)) {
if (this.useAntPath) {
((AntUrlPathMatcher) this.urlMatcher)
.setRequiresLowerCaseUrl(false);
}
}
System.out.println("this.urlMatcher" + this.urlMatcher);
}
public UrlMatcher getUrlMatcher() {
return urlMatcher;
}
public void setUrlMatcher(UrlMatcher urlMatcher) {
this.urlMatcher = urlMatcher;
}
public boolean isUseAntPath() {
return useAntPath;
}
public void setUseAntPath(boolean useAntPath) {
this.useAntPath = useAntPath;
}
public boolean isSetpathlowercase() {
return setpathlowercase;
}
public void setSetpathlowercase(boolean setpathlowercase) {
this.setpathlowercase = setpathlowercase;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
// TODO Auto-generated method stub
return null;
}
public boolean supports(Class<?> arg0) {
// TODO Auto-generated method stub
return true;
}
}