<Spring Security3>入门级详细配置
2013-06-07 19:07
344 查看
很早之前就听说了SpringSecurity,
但是一直没时间,最近花了几天时间试验了,感觉确实挺方便的。
研究过程中,虽然碰到了一些问题,但是最后还是解决了。
由于还没有研究源码,此篇文章入门使用。
我写这篇文章参考了
这是我使用的表结构
表名:RESOURCE解释:资源表
备注:资源表
RESOURCE(资源表) | ||||||||
是否主键 | 字段名 | 字段描述 | 数据类型 | 长度 | 可空 | 约束 | 缺省值 | 备注 |
是 | ID | id | INT(11) | 11 | ||||
TYPE | 类型(URL,METHOD) | VARCHAR(50) | 50 | 是 | ||||
VALUE | URL | VARCHAR(50) | 50 | 是 | ||||
MODEL_NAME | 模块名 | VARCHAR(50) | 50 | 是 | ||||
PARENT_ID | 父模块ID | VARCHAR(50) | 50 | 是 |
备注:角色表
ROLE(角色表) | ||||||||
是否主键 | 字段名 | 字段描述 | 数据类型 | 长度 | 可空 | 约束 | 缺省值 | 备注 |
是 | ID | id | INT(11) | 11 | ||||
NAME | 角色名 | VARCHAR(50) | 50 | 是 | ||||
DESCRIPTION | 角色描述 | VARCHAR(50) | 50 | 是 |
备注:角色资源表
ROLE_RESOURCE(角色资源表) | ||||||||
是否主键 | 字段名 | 字段描述 | 数据类型 | 长度 | 可空 | 约束 | 缺省值 | 备注 |
ROLE_ID | 角色ID | VARCHAR(50) | 50 | 是 | ||||
RESOURCE_ID | 资源ID | VARCHAR(50) | 50 | 是 |
备注:用户表
USER(用户表) | ||||||||
是否主键 | 字段名 | 字段描述 | 数据类型 | 长度 | 可空 | 约束 | 缺省值 | 备注 |
是 | NAME | 用户名 | VARCHAR(50) | 50 | ||||
PASSWORD | 密码 | VARCHAR(50) | 50 | 是 | ||||
DISABLED | 是否有效 | CHAR(1) | 1 | 是 | ||||
EMAIL | 邮箱 | VARCHAR(100) | 100 | 是 |
备注:用户角色表
USER_ROLE(用户角色表) | ||||||||
是否主键 | 字段名 | 字段描述 | 数据类型 | 长度 | 可空 | 约束 | 缺省值 | 备注 |
USER_ID | 用户ID | VARCHAR(50) | 50 | 是 | ||||
ROLE_ID | 角色ID | VARCHAR(50) | 50 | 是 |
下载链接:
首先web.xml配置
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/config/*.xml
</param-value>
</context-param>
<!--spring监听-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--SpringSecurity会话控制-->
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
<!--SpringsecurityFilter-->
<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>
这里主要做了三件事,
1、加载Spring;
2、加载SpringSecurity;
3、添加SpringSecuritySession监听器(用于控制登录)
SpringSecirty如下:
<?xmlversion="1.0"encoding="UTF-8"?>
<beans:beansxmlns="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/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<debug/>
<global-method-securitypre-post-annotations="enabled"/>
<!--此目录下不需要过滤-->
<httppattern="/js/**"security="none"/>
<httppattern="/resources/**"security="none"/>
<httppattern="/css/**"security="none"/>
<httppattern="/dwr/**"security="none"/>
<httppattern="/images/**"security="none"/>
<httppattern="/login.jsp"security="none"/>
<httpuse-expressions="true">
<!--非匿名用户就允许访问-->
<intercept-urlpattern="/index.jsp"access="isAuthenticated()"/>
<form-loginlogin-page="/login.jsp"authentication-failure-url="/login.jsp?error=true"always-use-default-target="true"default-target-url="/index.jsp"/>
<logoutlogout-success-url="/login.jsp"/>
<!--没有权限访问的页面-->
<access-denied-handlererror-page="/403.jsp"/>
<session-management></session-management>
<remember-me/>
<custom-filterref="myFilter"before="FILTER_SECURITY_INTERCEPTOR"/>
</http>
<!--指定提示信息-->
<beans:beanid="messageSource"class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:propertyname="basename"value="classpath:spring-security"></beans:property>
</beans:bean>
<authentication-manageralias="myAuthenticationManager">
<authentication-providerref="authenticationProvider">
</authentication-provider>
</authentication-manager>
<beans:beanid="authenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:propertyname="userDetailsService"ref="userdetailService"/>
<!--显示用户错误信息-->
<beans:propertyname="hideUserNotFoundExceptions"value="false"/>
<beans:propertyname="passwordEncoder"ref="md5password"></beans:property>
</beans:bean>
<!--密码加密策略-->
<beans:beanname="md5password"class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"></beans:bean>
<beans:beanname="userdetailService"class="com.yindejin.system.MyAuthenticationManager">
<beans:propertyname="systemService"ref="systemService"></beans:property>
</beans:bean>
<beans:beanname="myFilter"class="com.yindejin.system.MySecurityFilter">
<!--用户拥有的权限-->
<beans:propertyname="authenticationManager"ref="myAuthenticationManager"/>
<!--用户是否拥有所请求资源的权限-->
<beans:propertyname="accessDecisionManager"ref="myAccessDecisionManager"/>
<!--资源与权限对应关系-->
<beans:propertyname="securityMetadataSource"ref="mySecurityMetadataSource"/>
</beans:bean>
<beans:beanid="myAccessDecisionManager"class="com.yindejin.system.MyAccessDecisionManager"></beans:bean>
<beans:beanid="mySecurityMetadataSource"class="com.yindejin.system.MySecurityMetadataSource">
<beans:constructor-argname="systemService"ref="systemService"></beans:constructor-arg>
</beans:bean>
</beans:beans>
<custom-filterref="myFilter"before="FILTER_SECURITY_INTERCEPTOR"/>
这里的FILTER_SECURITY_INTERCEPTOR是SpringSecurity过滤器链中默认的Filter,
他的主要功能是
1、校验用户名、密码;
2、初始化时一次性加载所有的资源角色信息
3、检查用户访问权限
我们自定义的Filter必须在它之前,由于我们自己的过滤器和默认过滤器功能是一样的,所以就替换掉了原来的过滤器。
接下来是myFilter几个关键类
MySecurityFilter
packagecom.yindejin.system;
importjava.io.IOException;
importjavax.servlet.Filter;
importjavax.servlet.FilterChain;
importjavax.servlet.FilterConfig;
importjavax.servlet.ServletException;
importjavax.servlet.ServletRequest;
importjavax.servlet.ServletResponse;
importorg.springframework.security.access.SecurityMetadataSource;
importorg.springframework.security.access.intercept.AbstractSecurityInterceptor;
importorg.springframework.security.access.intercept.InterceptorStatusToken;
importorg.springframework.security.web.FilterInvocation;
importorg.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
publicclassMySecurityFilterextendsAbstractSecurityInterceptorimplementsFilter{
//与applicationContext-security.xml里的myFilter的属性securityMetadataSource对应,
//其他的两个组件,已经在AbstractSecurityInterceptor定义
privateFilterInvocationSecurityMetadataSourcesecurityMetadataSource;
@Override
publicSecurityMetadataSourceobtainSecurityMetadataSource(){
returnthis.securityMetadataSource;
}
publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,
FilterChainchain)throwsIOException,ServletException{
FilterInvocationfi=newFilterInvocation(request,response,chain);
invoke(fi);
}
privatevoidinvoke(FilterInvocationfi)throwsIOException,ServletException{
System.out.println("用户发送请求!");
InterceptorStatusTokentoken=null;
token=super.beforeInvocation(fi);
try{
fi.getChain().doFilter(fi.getRequest(),fi.getResponse());
}finally{
super.afterInvocation(token,null);
}
}
publicFilterInvocationSecurityMetadataSourcegetSecurityMetadataSource(){
returnsecurityMetadataSource;
}
publicvoidsetSecurityMetadataSource(FilterInvocationSecurityMetadataSourcesecurityMetadataSource){
this.securityMetadataSource=securityMetadataSource;
}
publicvoidinit(FilterConfigarg0)throwsServletException{
//TODOAuto-generatedmethodstub
}
publicvoiddestroy(){
//TODOAuto-generatedmethodstub
}
@Override
publicClass<?extendsObject>getSecureObjectClass(){
//下面的MyAccessDecisionManager的supports方面必须放回true,否则会提醒类型错误
returnFilterInvocation.class;
}}
MySecurityMetadataSource
packagecom.yindejin.system;
importjava.util.ArrayList;
importjava.util.Collection;
importjava.util.HashMap;
importjava.util.LinkedHashMap;
importjava.util.List;
importjava.util.Map;
importjava.util.Set;
importjavax.servlet.http.HttpServletRequest;
importorg.springframework.security.access.ConfigAttribute;
importorg.springframework.security.access.SecurityConfig;
importorg.springframework.security.web.FilterInvocation;
importorg.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
importorg.springframework.security.web.util.AntPathRequestMatcher;
importorg.springframework.security.web.util.RequestMatcher;
importcom.yindejin.system.service.ISystemService;
importcom.yindejin.vo.Resource;
importcom.yindejin.vo.Role;
importcom.yindejin.vo.User;
//1加载资源与权限的对应关系
publicclassMySecurityMetadataSourceimplementsFilterInvocationSecurityMetadataSource{
//由spring调用
publicMySecurityMetadataSource(ISystemServicesystemService){
this.systemService=systemService;
}
privateISystemServicesystemService;
publicISystemServicegetSystemService(){
returnsystemService;
}
publicvoidsetSystemService(ISystemServicesystemService){
this.systemService=systemService;
}
privatestaticLinkedHashMap<RequestMatcher,Collection<ConfigAttribute>>resourceMap=null;
publicCollection<ConfigAttribute>getAllConfigAttributes(){
//TODOAuto-generatedmethodstub
returnnull;
}
publicbooleansupports(Class<?>clazz){
//TODOAuto-generatedmethodstub
returntrue;
}
//加载所有资源与权限的关系
privateMap<String,String>getResource(){
Map<String,String>resourceMap=newHashMap<String,String>();
List<User>users=systemService.getAllUser();
for(Useruser:users){
for(Rolerole:user.getUserRoles()){
Set<Resource>resources=role.getRoleResources();
for(Resourceresource:resources){
Stringurl=resource.getValue();
if(!resourceMap.containsKey(url)){
resourceMap.put(url,role.getName());
}else{
StringroleName=resourceMap.get(url);
resourceMap.put(url,roleName+","+role.getName());
}
}
}
}
returnresourceMap;
}
privatevoidloadResourceDefine(){
resourceMap=newLinkedHashMap<RequestMatcher,Collection<ConfigAttribute>>();
Map<String,String>resource=getResource();
for(Map.Entry<String,String>entry:resource.entrySet()){
Collection<ConfigAttribute>configAttributes=newArrayList<ConfigAttribute>();
configAttributes.add(newSecurityConfig(entry.getValue()));
resourceMap.put(newAntPathRequestMatcher(entry.getKey()),configAttributes);
}
}
//返回所请求资源所需要的权限
publicCollection<ConfigAttribute>getAttributes(Objectobject)throwsIllegalArgumentException{
HttpServletRequestrequest=((FilterInvocation)object).getRequest();
if(null==resourceMap){
System.out.println("请求地址"+((FilterInvocation)object).getRequestUrl());
loadResourceDefine();
System.out.println("我需要的认证:"+resourceMap.toString());
}
for(Map.Entry<RequestMatcher,Collection<ConfigAttribute>>entry:resourceMap.entrySet()){
if(entry.getKey().matches(request)){
returnentry.getValue();
}
}
returnnull;
}
}
MyAccessDecisionManager
packagecom.yindejin.system;
importjava.util.Collection;
importjava.util.Iterator;
importorg.springframework.security.access.AccessDecisionManager;
importorg.springframework.security.access.AccessDeniedException;
importorg.springframework.security.access.ConfigAttribute;
importorg.springframework.security.authentication.InsufficientAuthenticationException;
importorg.springframework.security.core.Authentication;
importorg.springframework.security.core.GrantedAuthority;
//3
publicclassMyAccessDecisionManagerimplementsAccessDecisionManager{
publicvoiddecide(Authenticationauthentication,Objectobject,Collection<ConfigAttribute>configAttributes)throwsAccessDeniedException,InsufficientAuthenticationException{
if(configAttributes==null){
return;
}
//所请求的资源拥有的权限(一个资源对多个权限)
Iterator<ConfigAttribute>iterator=configAttributes.iterator();
while(iterator.hasNext()){
ConfigAttributeconfigAttribute=iterator.next();
//访问所请求资源所需要的权限
StringneedPermission=configAttribute.getAttribute();
System.out.println("needPermissionis"+needPermission);
//用户所拥有的权限authentication
for(GrantedAuthorityga:authentication.getAuthorities()){
if(needPermission.contains((ga.getAuthority()))){
return;
}
}
}
//没有权限让我们去捕捉
thrownewAccessDeniedException("没有权限访问!");
}
publicbooleansupports(ConfigAttributeattribute){
//TODOAuto-generatedmethodstub
returntrue;
}
publicbooleansupports(Class<?>clazz){
//TODOAuto-generatedmethodstub
returntrue;
}
}
MyAuthenticationManager
packagecom.yindejin.system;
importorg.springframework.security.core.userdetails.UserDetails;
importorg.springframework.security.core.userdetails.UserDetailsService;
importorg.springframework.security.core.userdetails.UsernameNotFoundException;
importcom.yindejin.system.service.ISystemService;
importcom.yindejin.util.StringUtils;
importcom.yindejin.vo.User;
publicclassMyAuthenticationManagerimplementsUserDetailsService{
privateISystemServicesystemService;
publicvoidsetSystemService(ISystemServicesystemService){
this.systemService=systemService;
}
@Override
publicUserDetailsloadUserByUsername(StringuserName)throwsUsernameNotFoundException{
if(!StringUtils.isEmpty(userName)){
thrownewUsernameNotFoundException("用户名不能为空!");
}
Useruser=systemService.loginByUserName(userName);
if(user==null){
thrownewUsernameNotFoundException("用户名或密码错误!");
}
returnuser;
}
}
User
packagecom.yindejin.vo;
importjava.util.ArrayList;
importjava.util.Collection;
importjava.util.HashMap;
importjava.util.HashSet;
importjava.util.List;
importjava.util.Map;
importjava.util.Set;
importjavax.persistence.Transient;
importorg.springframework.security.core.GrantedAuthority;
importorg.springframework.security.core.authority.GrantedAuthorityImpl;
importorg.springframework.security.core.userdetails.UserDetails;
/**
*Userentity.@authorMyEclipsePersistenceTools
*/
publicclassUserimplementsUserDetails{
//Fields
privateIntegerid;
privateStringname;
privateStringpassword="123456";
privateIntegerdisabled=0;
privateStringemail;
publicStringgetEmail(){
returnemail;
}
publicvoidsetEmail(Stringemail){
this.email=email;
}
privateSet<Role>userRoles=newHashSet<Role>();
@Transient
privateMap<String,List<Resource>>roleResources;
//Constructors
publicIntegergetId(){
returnid;
}
publicvoidsetId(Integerid){
this.id=id;
}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
publicStringgetPassword(){
returnpassword;
}
publicvoidsetPassword(Stringpassword){
this.password=password;
}
publicIntegergetDisabled(){
returndisabled;
}
publicvoidsetDisabled(Integerdisabled){
this.disabled=disabled;
}
publicSet<Role>getUserRoles(){
returnuserRoles;
}
publicvoidsetUserRoles(Set<Role>userRoles){
this.userRoles=userRoles;
}
/**defaultconstructor*/
publicUser(){
}
publicStringgetUsername(){
returnname;
}
publicbooleanisAccountNonExpired(){
returntrue;
}
publicbooleanisAccountNonLocked(){
returntrue;
}
publicbooleanisCredentialsNonExpired(){
returntrue;
}
publicbooleanisEnabled(){
returnthis.disabled==0?true:false;
}
/**
*@returntheroleResources
*/
publicMap<String,List<Resource>>getRoleResources(){
//initroleResourcesforthefirsttime
if(this.roleResources==null){
this.roleResources=newHashMap<String,List<Resource>>();
for(Rolerole:this.userRoles){
StringroleName=role.getName();
Set<Resource>resources=role.getRoleResources();
for(Resourceresource:resources){
Stringkey=roleName+"_"+resource.getType();
if(!this.roleResources.containsKey(key)){
this.roleResources.put(key,newArrayList<Resource>());
}
this.roleResources.get(key).add(resource);
}
}
}
returnthis.roleResources;
}
@SuppressWarnings("deprecation")
@Override
publicCollection<GrantedAuthority>getAuthorities(){
Set<GrantedAuthority>authSet=newHashSet<GrantedAuthority>();
for(Rolerole:getUserRoles()){
authSet.add(newGrantedAuthorityImpl(role.getName()));
}
returnauthSet;
}
}
结束语:
自己的第一篇博客,本人不才,还请大家指正。
相关文章推荐
- 只有在配置文件或 Page 指令中将 enableSessionState 设置为 true 时,才能使用会话状态。还请确保在应用程序配置的 <configuration>
- 关于Spring中的<context:annotation-config/>配置
- <<svn服务配置和维护常用命令>>
- applicationContext-security.xml配置中的<http></http>标签的意义
- <<BASM 初学者入门>> 第 1 课 A
- <PY><core python programming笔记>C2 快速入门
- CentOS64+Qt+Mysql+ftp+无密钥传输的配置<详细过程>
- &lt;展现C#&gt;第九章配置和调度
- C语言指针教程----入门到精通<一>
- <Unity3D>Unity3D入门篇——第五讲 GUI控件(三)
- rhel_rsync详细配置<二>rsync命令行操作
- <Flash入门>对AS语言的一些理解
- 网页制作简单入门——用<div>设计简单模块
- [原]java专业程序代写(qq:928900200),学习笔记之基础入门<Hibernate_配置详解>(三十六)
- 关于Spring中的<context:annotation-config/>配置
- <PY>iPad/iPhone上Python配置
- <<BASM 初学者入门>> 第 1 课 C
- <Unity3D>Unity3D入门篇——第四讲 GUI控件(二)
- rhel_rsync详细配置<三>rsync服务器配置
- 关于Spring中的<context:annotation-config/>配置