`
aokunsang
  • 浏览: 812634 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Spring Security3.1实践

阅读更多

   说明下:  本篇博客时间久远,数据库已被我重装系统删除。本人懒蛋一个,不想建了,如果学习使用的童鞋根据表结构自行建立吧。

 

 

 

   本例子是我在spring MVC3.0.5的基础上进行修改的,用的Spring Security3.1.2。

 

          数据库:mysql,开发工具:myeclipse8.6,tomcat6.0。

 

1.收集资料

http://blog.csdn.net/k10509806/article/details/6369131

http://www.cnblogs.com/wenxiu/archive/2011/01/22/1942084.html

http://ootabc.iteye.com/blog/688213

http://wenku.baidu.com/view/abf23846336c1eb91a375d83.html

http://www.cnblogs.com/zhangliang0115/archive/2012/04/02/2429584.html

2.数据库建表

采用基于角色-资源-用户权限管理设计。

2.1.用户表    test_user

字段名

标示符

类型

有无空值

主键

备注

ID

ID

varchar(36)

NO

Y

编号

USERNAME

用户名

Varchar(30)

NO

 

 

PASSWORD

密码

varchar(36)

NO

 

 

STATUS

状态

tinyint

 

 

0开启、

1关闭

2.2.角色表    test_role

字段名

标示符

类型

有无空值

主键

备注

ID

ID

varchar(36)

NO

Y

编号

NAME

角色名

varchar(50)

 

 

 

2.3.资源表    test_resource

字段名

标示符

类型

有无空值

主键

备注

ID

ID

varchar(36)

NO

Y

编号

NAME

资源名称

varchar(50)

 

 

 

URL

地址

Varchar(50)

 

 

 

TYPE

类型

Tinyint

 

 

 

2.4.用户角色表   test_user_role

字段名

标示符

类型

有无空值

主键

备注

ID

ID

varchar(36)

NO

Y

编号

UID

用户ID

varchar(36)

 

 

 

RID

角色ID

varchar(36)

 

 

 

2.5.角色资源表   test_role_resource

字段名

标示符

类型

有无空值

主键

备注

ID

ID

varchar(36)

NO

Y

编号

RSID

资源ID

varchar(36)

 

 

 

RID

角色ID

varchar(36)

 

 

 

3.梳理资料,整理思路

3.1.Spring Security3.1的2种常见方式

Ø  用户信息和权限存储于数据库,而资源和权限的对应采用硬编码配置。

Ø  细分角色和权限,并将角色、用户、资源、权限均都存储于数据库中。并且自定义过滤器,代替原来的FilterSecurityInterceptor过滤器;并分别实现AccessDecisionManager、UserDetailsService和InvocationSecurityMetadataSourceService,并在配置文件中进行相应配置。

Ø发现两者不可结合使用,会有问题。

4.代码整理

接下来开始着手代码编写,不管是两种实现方式中的哪种方式,个人感觉都需要把加载用户信息放在一个类里面管理,直观方便,结构清晰,不要用在配置文件直接写sql语句。

4.1.资源和权限对应写在配置文件中

1、     web.xml配置

     a)     启动时加载Spring的jdbc和security配置文件。

     b)      配置spring的servlet过滤器,使其能够识别Spring Controller。

     c)     加载Spring Security过滤器链代理,它按照顺序执行spring的权限过滤器。

     d)     其他业务加载,比如:log4j,字符集编码过滤器,session超时等。

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
	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_2_5.xsd">
  
   <context-param>
		<param-name>webAppRootKey</param-name>
		<param-value>springMvc</param-value>
   </context-param>
   
 	<!-- Listener log4jConfigLocation -->
	<listener>
		<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
	</listener>
  
  <context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		classpath:/module/applicationContext-jdbc.xml,
		classpath:/module/applicationContext-security.xml
	</param-value>
  </context-param>
  
  <!-- Log4j -->
  <context-param>
  	<param-name>log4jConfigLocation</param-name>
  	<param-value>classpath:/config/log4j.properties</param-value>
  </context-param>
  
  <listener>
  	<listener-class>org.springframework.web.context.ContextLoaderListener</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>
  
  <servlet>
	  	<servlet-name>springmvc</servlet-name>
	  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	  	<init-param>
	  		<param-name>contextConfigLocation</param-name>
	  		<param-value>classpath:/module/applicationContext-servlet.xml</param-value>
	  	</init-param>
	  	<load-on-startup>1</load-on-startup>
	</servlet>
  
    <servlet-mapping>
	  	<servlet-name>springmvc</servlet-name>
	  	<url-pattern>/</url-pattern>
    </servlet-mapping>
  
  <!-- Spring 刷新Introspector防止内存泄露 -->
	<listener>
		<listener-class>
			org.springframework.web.util.IntrospectorCleanupListener
		</listener-class>
	</listener>
	
	<!--  获取Spring Security session的生命周期-->
  	<listener>
		<listener-class>
			org.springframework.security.web.session.HttpSessionEventPublisher 
		</listener-class>
	</listener>

	<!-- session超时定义,单位为分钟 -->
	<session-config>
		<session-timeout>20</session-timeout>
	</session-config>
  
  
  <filter>
  	<filter-name>encodingFilter</filter-name>
  	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  </filter>
  
  <filter-mapping>
  	<filter-name>encodingFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

 

 

  2、  application-security.xml文件的配置。application-servlet.xml配置不懂的参考spring MVC3.0.5搭建全程。

 

<?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">
 
  <http pattern="/resources/**" security="none"></http>	
  <http pattern="/user/login" security="none"></http>
  
  <http auto-config="true"
   		use-expressions="true" 
   		access-denied-page="/user/denied">
  <!-- 
  	default-target-url       指定了从登录页面登录后进行跳转的页面
  	always-use-default-target   true表示登录成功后强制跳转
  	authentication-failure-url  表示验证失败后进入的页面
  	login-processing-url       设置验证登录验证地址,如果不设置,默认是j_spring_security_check
  	username-parameter,password-parameter     设置登录用户名和密码的请求name,默认:j_username,j_password
  	default-target-url="/user/home" 
   -->
  	<form-login login-page="/user/login"
  				always-use-default-target="true"
  				authentication-failure-url="/user/login?error=1"
  				login-processing-url="/logincheck"
  				authentication-success-handler-ref="successHandler"/>
  	
	<intercept-url pattern="/user/myjsp" access="hasRole('ROLE_USER')"/>
	<intercept-url pattern="/user/admin" access="hasRole('ROLE_ADMIN')"/>
	
  	<logout logout-url="/logout" logout-success-url="/user/login"/>
  	<!-- 
  		 error-if-maximum-exceeded 后登陆的账号会挤掉第一次登陆的账号 
  	     session-fixation-protection  防止伪造sessionid攻击,用户登录成功后会销毁用户当前的session。
  	-->
  	<session-management invalid-session-url="/user/timedout" session-fixation-protection="none">
  		<concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>
  	</session-management>
  	<!-- <custom-filter ref="mySecurityFilter" before="FILTER_SECURITY_INTERCEPTOR"/> -->
  </http>
  
  <authentication-manager alias="authManager">
  	<authentication-provider user-service-ref="userServiceDetail">
  		<!--<jdbc-user-service data-source-ref="dataSource" 
  							authorities-by-username-query=""
  							group-authorities-by-username-query=""/> -->
  		<password-encoder hash="md5">
  			<salt-source user-property="username"/>   <!-- 盐值  [添加这个属性后,加密密码明文为:"密码明文{盐值}"] -->
  		</password-encoder>
  		
  	</authentication-provider>
  </authentication-manager>
  
</beans:beans>

问题:

 

    我自己写了个User实现UserDetails,发现同一个账号可以同时登陆,也就是说concurrency-control没有起到作用,参考了一下资料后,重写一下User的hashcode,equals方法就行了。【后来发现的问题,附件自己添加】

 

    详细参考:http://flashing.iteye.com/blog/823666

 

 

	
	@Override
	public int hashCode() {
		return username.hashCode();
	}
	
	@Override
	public boolean equals(Object obj) {
		User user = (User)obj;
		return this.username.equals(user.getUsername());
	}

 

解析:

     a、use-expressions

如:hasRole(‘ROLE_ADMIN’或hasIpAddress(‘127.0.0.1’))等,看不懂的可以参考下面链接。

http://static.springsource.org/spring-security/site/docs/3.0.7.RELEASE/reference/el-access.html

http://hougbin.iteye.com/blog/1526980

http://kongcodecenter.iteye.com/blog/1320021

   b、<password-encoder  hash=”md5”>

其属性hash就是加密的方法是什么?常用的可能是md5和sha吧。

主要说下<salt-source user-property=’username’>盐值:不加这个属性,spring验证密码时,直接用MD5加密后的值,与我们自己写的实现了UserDetailsService接口的类中loadUsersByUsername(String username)方法返回的UserDetails中的密码进行比较;如果加了这个属性并且设置user-property=’username’[不知道能不能设置其他值,或许也可以设置password,没有尝试],加密前的明文就成为”密码明文{盐值}”,这里的盐值为用户名。

   c、remember-me的实现策略参考下面:

     http://static.springsource.org/spring-security/site/docs/3.0.x/reference/remember-me.html

     http://blog.csdn.net/small_love/article/details/6641316

     http://xyz20003.iteye.com/blog/223282

   d、UserDetailsService可以通过手工设置几个用户的权限:

<user-service id="userDetailsService">
    <user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
    <user name="bob" password="bobspassword" authorities="ROLE_USER" />
  </user-service>

     或者通过属性文件读取;

    <user-service id="userDetailsService" properties="users.properties"/>

     属性 文件内容格式为: username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]

jimi=jimispassword,ROLE_USER,ROLE_ADMIN,enabled
bob=bobspassword,ROLE_USER,enabled

   

   e、UserDetailsService实现类

 

/**
 * @description  项目实现的用户查询服务,将用户信息查询出来(用于实现用户的认证)
 * @author aokunsang
 * @date 2012-8-15
 */
public class MyUserDetailServiceImpl implements UserDetailsService {

	private UserService userService;
	
	@Override
	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException {
		
		System.out.println("---------MyUserDetailServiceImpl:loadUserByUsername------正在加载用户名和密码,用户名为:"+username);
		
		User user = userService.loadUserByUserName(username);
		if(user==null){
			throw new UsernameNotFoundException("用户名没有找到!");
		}
		
		boolean enabled = true;                //是否可用
        boolean accountNonExpired = true;        //是否过期
        boolean credentialsNonExpired = true;   
        boolean accountNonLocked = true;  
		
		Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
		//如果你使用资源和权限配置在xml文件中,如:<intercept-url pattern="/user/admin" access="hasRole('ROLE_ADMIN')"/>;
		//并且也不想用数据库存储,所有用户都具有相同的权限的话,你可以手动保存角色(如:预订网站)。
		//authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
		
		List<Role> roles = userService.findUserRolesByUsername(username);
		for(Role role : roles){
			GrantedAuthority ga = new SimpleGrantedAuthority(role.getName());
			authorities.add(ga);	
		}
		return new org.springframework.security.core.userdetails.User(
				user.getUserName(),
				user.getPassWord(), 
				enabled, 
				accountNonExpired, 
				credentialsNonExpired, 
				accountNonLocked, 
				authorities);
	}
	/**
	 * @param userService the userService to set
	 */
	public void setUserService(UserService userService) {
		this.userService = userService;
	}

}

 

4.2.资源和配置文件存储在数据库中

需要自己手动写一个拦截器,提供查询数据库中的资源权限,提供验证用户是否具有访问URL地址的权限(3个类)。

        1、AbstractSecurityInterceptor继承类,同时实现Filter接口。

 

/**
 * @description 一个自定义的filter,
 * 	必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性,
    	我们的所有控制将在这三个类中实现
 * @author aokunsang
 * @date 2012-8-15
 */
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor
		implements Filter {

	private FilterInvocationSecurityMetadataSource fisMetadataSource;
	
	/* (non-Javadoc)
	 * @see org.springframework.security.access.intercept.AbstractSecurityInterceptor#getSecureObjectClass()
	 */
	@Override
	public Class<?> getSecureObjectClass() {
		return FilterInvocation.class;
	}

	@Override
	public SecurityMetadataSource obtainSecurityMetadataSource() {
		return fisMetadataSource;
	}

	@Override
	public void destroy() {}
	
	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
        //super.beforeInvocation(fi);源码  
		//1.获取请求资源的权限  
			//执行Collection<ConfigAttribute> attributes = SecurityMetadataSource.getAttributes(object);  
		//2.是否拥有权限  
			//this.accessDecisionManager.decide(authenticated, object, attributes);  
		System.out.println("------------MyFilterSecurityInterceptor.doFilter()-----------开始拦截器了....");
		FilterInvocation fi = new FilterInvocation(request, response, chain);
		InterceptorStatusToken token = super.beforeInvocation(fi);
		try {
			fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			super.afterInvocation(token,null);
		}
		System.out.println("------------MyFilterSecurityInterceptor.doFilter()-----------拦截器该方法结束了....");
	}
 
	@Override
	public void init(FilterConfig config) throws ServletException {
		
	}
	
	
	public void setFisMetadataSource(
			FilterInvocationSecurityMetadataSource fisMetadataSource) {
		this.fisMetadataSource = fisMetadataSource;
	}
}

 

        2、FilterInvocationSecurityMetadataSource实现类

 

/**
 * @description  资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问
 * @author aokunsang
 * @date 2012-8-15
 */
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

	private UserService userService;
	/* 保存资源和权限的对应关系  key-资源url  value-权限 */
	private Map<String,Collection<ConfigAttribute>> resourceMap = null; 
	private AntPathMatcher urlMatcher = new AntPathMatcher();
	
	public MySecurityMetadataSource(UserService userService) {
		this.userService = userService;
		loadResourcesDefine();
	}
	
	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		return null;
	}

	private void loadResourcesDefine(){
		resourceMap = new HashMap<String,Collection<ConfigAttribute>>();
//		Collection<ConfigAttribute> configAttributes1 = new ArrayList<ConfigAttribute>() ;
//		ConfigAttribute configAttribute1 = new SecurityConfig("ROLE_ADMIN");
//		configAttributes1.add(configAttribute1);
//		resourceMap.put("/leftmenu.action", configAttributes1);
		
		System.out.println("MySecurityMetadataSource.loadResourcesDefine()--------------开始加载资源列表数据--------");
		List<Role> roles = userService.findAllRoles();
		for(Role role : roles){
			List<Resource> resources = userService.findResourcesByRoleName(role.getName());
			for(Resource resource : resources){
				Collection<ConfigAttribute> configAttributes = null;
				ConfigAttribute configAttribute = new SecurityConfig(role.getName());
				if(resourceMap.containsKey(resource.getUrl())){
					configAttributes = resourceMap.get(resource.getUrl());
					configAttributes.add(configAttribute);
				}else{
					configAttributes = new ArrayList<ConfigAttribute>() ;
					configAttributes.add(configAttribute);
					resourceMap.put(resource.getUrl(), configAttributes);
				}
			}
		}
	}
	/* 
	 * 根据请求的资源地址,获取它所拥有的权限
	 */
	@Override
	public Collection<ConfigAttribute> getAttributes(Object obj)
			throws IllegalArgumentException {
		//获取请求的url地址
		String url = ((FilterInvocation)obj).getRequestUrl();
		System.out.println("MySecurityMetadataSource:getAttributes()---------------请求地址为:"+url);
		Iterator<String> it = resourceMap.keySet().iterator();
		while(it.hasNext()){
			String _url = it.next();
			if(_url.indexOf("?")!=-1){
				_url = _url.substring(0, _url.indexOf("?"));
			}
			if(urlMatcher.match(_url,url))
				return resourceMap.get(_url);
		}
		return null;
	}

	@Override
	public boolean supports(Class<?> arg0) {
		System.out.println("MySecurityMetadataSource.supports()---------------------");
		return true;
	}
	
}

   

       3、AccessDecisionManager实现类

 

/**
 * @description  访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 ;做最终的访问控制决定
 * @author aokunsang
 * @date 2012-8-16
 */
public class MyAccessDescisionManager implements AccessDecisionManager {

	/**
	 * @description 认证用户是否具有权限访问该url地址
	 * 
	 */
	@Override
	public void decide(Authentication authentication, Object obj,
			Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,InsufficientAuthenticationException {
		System.out.println("MyAccessDescisionManager.decide()------------------验证用户是否具有一定的权限--------");
		if(configAttributes==null) return;
		Iterator<ConfigAttribute> it = configAttributes.iterator();
		while(it.hasNext()){
			String needRole = it.next().getAttribute();
			//authentication.getAuthorities()  用户所有的权限
			for(GrantedAuthority ga:authentication.getAuthorities()){
				if(needRole.equals(ga.getAuthority())){
					return;
				}
			}
		}
		throw new AccessDeniedException("--------MyAccessDescisionManager:decide-------权限认证失败!");
	}

	/**
	 * 启动时候被AbstractSecurityInterceptor调用,决定AccessDecisionManager是否可以执行传递ConfigAttribute。
	 */
	@Override
	public boolean supports(ConfigAttribute configAttribute) {
		System.out.println("MyAccessDescisionManager.supports()------------角色名:"+configAttribute.getAttribute());
		return true;
	}

	/**
	 * 被安全拦截器实现调用,包含安全拦截器将显示的AccessDecisionManager支持安全对象的类型
	 */
	@Override
	public boolean supports(Class<?> clazz) {
		System.out.println("MyAccessDescisionManager.supports()--------------------------------");
		return true;
	}

}

 

补充:还可以实现AuthenticationFailureHandler和AuthenticationSuccessHandler这两个接口,可以做一些验证失败和成功后的业务逻辑操作。(注意实现了这两个接口后,需要手动跳转路径),在<form-login>里面可配置。

        4、修改配置文件。

 

<?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">
 
  <http pattern="/resources/**" security="none"></http>	
  <http pattern="/user/login" security="none"></http>
  
  <http auto-config="true" access-denied-page="/user/denied">
  <!-- 
  	default-target-url       指定了从登录页面登录后进行跳转的页面
  	always-use-default-target   true表示登录成功后强制跳转
  	authentication-failure-url  表示验证失败后进入的页面
  	login-processing-url       设置验证登录验证地址,如果不设置,默认是j_spring_security_check
  	username-parameter,password-parameter     设置登录用户名和密码的请求name,默认:j_username,j_password
  	default-target-url="/user/home" 
   -->
  	<form-login login-page="/user/login"
  				always-use-default-target="true"
  				authentication-failure-url="/user/login?error=1"
  				login-processing-url="/logincheck"
  				authentication-success-handler-ref="successHandler"/>
  	
  	<logout logout-url="/logout" logout-success-url="/user/login"/>
  	<!-- 
  		 error-if-maximum-exceeded 后登陆的账号会挤掉第一次登陆的账号 
  	     session-fixation-protection  防止伪造sessionid攻击,用户登录成功后会销毁用户当前的session。
  	-->
  	<session-management invalid-session-url="/user/timedout" session-fixation-protection="none">
  		<concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>
  	</session-management>
   <custom-filter ref="mySecurityFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
  </http>
  
  <authentication-manager alias="authManager">
  	<authentication-provider user-service-ref="userServiceDetail">
  		<!--<jdbc-user-service data-source-ref="dataSource" 
  							authorities-by-username-query=""
  							group-authorities-by-username-query=""/> -->
  		<password-encoder hash="md5">
  			<salt-source user-property="username"/>   <!-- 盐值  [添加这个属性后,加密密码明文为:"密码明文{盐值}"] -->
  		</password-encoder>
  		
  	</authentication-provider>
  </authentication-manager>
  
  
  <!-- 登录失败后业务处理 -->
  <beans:bean id="failureHandler" class="com.aokunsang.security.LoginAuthenticationFailureHandler"></beans:bean>
  <!-- 登录成功业务处理 -->
  <beans:bean id="successHandler" class="com.aokunsang.security.LoginAuthenticationSuccesssHandler">
  	<beans:property name="defaultUrl" value="/user/admin"></beans:property>  <!-- 可变换登录成功后的路径,验证用户是否拥有该权限 -->
  </beans:bean>
  
  <!-- 自定义过滤器  -->
  <beans:bean id="mySecurityFilter" class="com.aokunsang.security.MyFilterSecurityInterceptor">
  	<beans:property name="accessDecisionManager" ref="accessDescisionManager"></beans:property>
  	<beans:property name="fisMetadataSource" ref="securityMetadataSource"></beans:property>
  	<beans:property name="authenticationManager" ref="authManager"></beans:property>
  </beans:bean>
  
  <beans:bean id="securityMetadataSource" class="com.aokunsang.security.MySecurityMetadataSource">
  	<beans:constructor-arg name="userService" ref="userService"></beans:constructor-arg>
  </beans:bean>
  
  <beans:bean id="accessDescisionManager" class="com.aokunsang.security.MyAccessDescisionManager"></beans:bean>
     
  <beans:bean id="userServiceDetail" class="com.aokunsang.security.MyUserDetailServiceImpl">
  	<beans:property name="userService">
		<beans:ref bean="userService"/>
  	</beans:property>
  </beans:bean>
</beans:beans>

 

4.3.替换form-login配置,实现自己的业务逻辑

如果想在登录之前做一些业务逻辑操作,比如:检查验证码的正确性(这个操作肯定要在验证用户名密码之前操作了)。那么我们自己继承UsernamePasswordAuthenticationFilter类,替换form-login里面的配置,完成检查验证码的操作;这里还需要注意一点,我们还需要实现一个未登录的切点(配置AuthenticationProcessingFilterEntryPoint或者LoginUrlAuthenticationEntryPoint),也就是没登录的都跳转到这个页面,相当于<form-login>中的login-page属性。

这里面的配置注意两点就行:

       1、在<http>中添加未登录切点配置entry-point-ref属性;

       2、去掉<form-login>,添加<custom-filter ref="XXXXFilter" position="FORM_LOGIN_FILTER"/>  

 

 说明:详细使用方法以及用户账户登录数控制<session-management><concurrency-control></session-management>可参考另一博客http://aokunsang.iteye.com/blog/1944111

 

http://blog.csdn.net/k10509806/article/details/6436987

http://hi.baidu.com/youxitou/item/de0fb00e76e15095a2df43cd

     

4.4.补充问题汇总

          在项目中使用spring Security3.1时,发现抛出的UsernameNotFoundException异常信息,总是打印出Bad credentials。如果我想得到比如:用户不存在,等信息,需要在xml中做设置。

 

<!-- 使用该类主要解决例如UsernameNotFoundException抛出的异常全部显示Bad credentials[详细参考AbstractUserDetailsAuthenticationProvider:authenticate()];
  	         注意:如果通过用户名已经查询到用户信息(密码错误),此时抛出异常依然为Bad credentials[详细参考DaoAuthenticationProvider:additionalAuthenticationChecks()]
   -->
  <beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">  
    <beans:property name="userDetailsService" ref="userServiceDetail"></beans:property> 
    <beans:property name="passwordEncoder" ref="md5Encoder"></beans:property> 
    <beans:property name="hideUserNotFoundExceptions" value="false"/><!-- 【关键】没有这个将不能准确地报告异常(全部报告异常为:Bad credentials) -->  
   </beans:bean>
  
  <beans:bean id="md5Encoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"></beans:bean>
  
  <authentication-manager alias="authManager">
  	<authentication-provider ref="daoAuthenticationProvider"></authentication-provider>
  </authentication-manager>
  

 

 

5.附录

5.1.默认请求参数说明

默认值

说明

可设置

j_username

请求用户名

<from_login/>中

username_parameter

属性

j_password

请求密码

<from_login/>中

password_parameter

属性

j_spring_security_check

Post请求验证路径

<from_login/>中

login_processing_url属性

_spring_security_remember_me

“记住我”的请求name

暂无

sessionScope['SPRING_SECURITY_LAST_USERNAME']

 

Session中保存的最后一次登录的用户名

暂无

 

 

 

分享到:
评论
29 楼 -Ian 2015-08-13  
您好,您能具体讲一讲,如何更新权限map吗。
28 楼 aokunsang 2015-05-30  
beelzebub 写道
有个问题:角色和模块的关系是在启动的时候建立的。一般的程序都允许后台设置,新建立的关系就不能被使用。这个怎么解决?

1、这个简单,在新加或者更新关系的时候,重新加载权限列表。
PmcSecurityMetadataSource pmcMetadataSource = new PmcSecurityMetadataSource(userService);
pmcMetadataSource.loadResourcesDefine();
2、你可能需要注意的是,用户修改了密码怎么操作。
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(username,new_password));
27 楼 beelzebub 2015-05-28  
有个问题:角色和模块的关系是在启动的时候建立的。一般的程序都允许后台设置,新建立的关系就不能被使用。这个怎么解决?
26 楼 CJ66 2014-11-06  
楼主,可以发一份数据库给我吗。发一些有数据的,我想debug一下程序。这样就会更熟悉流程了。315730115@qq.com...  在此非常感谢。。
25 楼 qq4285855 2014-07-17  
楼主,可以发一份数据库给我吗。发一些有数据的,我想debug一下程序。这样就会更熟悉流程了。819931129@qq.com...  在此非常感谢。。
24 楼 c.hle2008 2014-07-12  
<form-login login-page="/login.jsp" default-target-url="/main.jsp"  always-use-default-target="true" authentication-failure-url="/login.jsp"  login-processing-url="/user/login" />

我的login-processing-url不起作用啊,求LZ帮忙解决下问题呀,我的Q:240691543
23 楼 herofighter2008 2014-06-30  
你好,可以加下我QQ吗?381885230,有问题想向你请教,谢谢
22 楼 aokunsang 2014-01-11  
zks5188 写道
  
    private UserService userService;  
    /* 保存资源和权限的对应关系  key-资源url  value-权限 */  
    private Map<String,Collection<ConfigAttribute>> resourceMap = null;   
    private AntPathMatcher urlMatcher = new AntPathMatcher();  
      
    public MySecurityMetadataSource(UserService userService) {  
        this.userService = userService;  
        loadResourcesDefine();  
    }  
      

楼主你好,这段代码中,我使用MySecurityMetadataSource()来构造,然后想要使用
@autoware
private UserService userService;  

来注入,但是注入的一直都是NULL,有可能是什么原因?
我这边是SpringMVC+SS+Hibernate

MySecurityMetadataSource是在项目启动的时候执行的,那么你需要保证spring已经扫描了Userservice类,也就是spring已经加载了springApplicationContext.xml文件。 这里面的优先级问题,我也没有深究,你可以研究下。
21 楼 zks5188 2014-01-10  
  
    private UserService userService;  
    /* 保存资源和权限的对应关系  key-资源url  value-权限 */  
    private Map<String,Collection<ConfigAttribute>> resourceMap = null;   
    private AntPathMatcher urlMatcher = new AntPathMatcher();  
      
    public MySecurityMetadataSource(UserService userService) {  
        this.userService = userService;  
        loadResourcesDefine();  
    }  
      

楼主你好,这段代码中,我使用MySecurityMetadataSource()来构造,然后想要使用
@autoware
private UserService userService;  

来注入,但是注入的一直都是NULL,有可能是什么原因?
我这边是SpringMVC+SS+Hibernate
20 楼 wuxiandeng 2013-12-20  
qq852278685,求数据库和数据
19 楼 aokunsang 2013-12-16  
caozhenndsc 写道
博主这篇博文很不错啊,不过有个小地方可能有问题,请楼主看下
MySecurityMetadataSource的方法getAttributes
if(_url.indexOf("?")!=-1){ 
  _url = _url.substring(0, _url.indexOf("?")); 


应该改成url吧,当前请求

没问题的,这里的操作是分解数据库中的url(去掉参数),只要url地址,去比对当前url。你再仔细阅读下。
18 楼 caozhenndsc 2013-12-15  
博主这篇博文很不错啊,不过有个小地方可能有问题,请楼主看下
MySecurityMetadataSource的方法getAttributes
if(_url.indexOf("?")!=-1){ 
  _url = _url.substring(0, _url.indexOf("?")); 


应该改成url吧,当前请求
17 楼 aokunsang 2013-12-12  
haifengyouxi 写道
------------MyFilterSecurityInterceptor.doFilter()-----------开始拦截器了....
MySecurityMetadataSource:getAttributes()---------------请求地址为:/index.jsp
------------MyFilterSecurityInterceptor.doFilter()-----------拦截器该方法结束了....
------------MyFilterSecurityInterceptor.doFilter()-----------开始拦截器了....
MySecurityMetadataSource:getAttributes()---------------请求地址为:/user/admin
------------MyFilterSecurityInterceptor.doFilter()-----------拦截器该方法结束了....
---------MyUserDetailServiceImpl:loadUserByUsername------正在加载用户名和密码,用户名为:test_u
--------------------------验证失败,重新登录中.......


博主,你好。感谢你的博文,让我对spring sec了解到了不少。下面是我在调试过程中遇到的一些问题:
1、上面这段控制台提示分别是我访问主页,返回了登录主界面,这没问题。
2、访问/user/admin(还没有登录),浏览器显示了admin.jsp的页面内容,这里是否起到了拦截的作用?
3、登录测试,返回如上结果。

能否发一份带测试数据的数据库给我?谢谢。
mail:cataf@foxmail.com

你好,博客能给你带来好处,深感欣慰,大家共同学习。
由于该博客写的时间比较久,期间我进行了全面的升级,且Mysql数据库重装系统后已丢失,只能由你自己建立。
如果直接输入地址/user/admin进入,说明有些问题。因为/user/admin需要ROLE_ADMIN权限.
16 楼 aokunsang 2013-12-12  
keaco204 写道
楼主,你好,我在用你上面给的 <http auto-config="true" access-denied-page="/user/denied"> 定义没有权限时403的页面,出现了下面的异常,A universal match pattern ('/**') is defined  before other patterns in the filter chain, causing them to be ignored. Please check the ordering in your <security:http> namespace or FilterChainProxy bean configuration,如果没有配置403页面就没问题。还有一个就是想问一下hasIpAddress()这个,括号里那个掩码是怎么解释的,http://forum.spring.io/forum/spring-projects/security/95303-how-to-use-hasipaddress,这个网址上的问题跟我现在的问题差不多,但是我看了一下那个回答,还是 不太明白,求教

对第一个问题,我没遇到过。你可以试试,在<http></http>里面加入<access-denied-handler error-page="/user/denied"/>试试是否可行。
第二个问题,hasIpAddress()这个玩意我几乎没用过,在刚学习的时候测试过,没深究。如果想了解,可参考官方说明:http://static.springsource.org/spring-security/site/docs/3.0.7.RELEASE/reference/el-access.html。你可能会有收获。
15 楼 haifengyouxi 2013-12-10  
------------MyFilterSecurityInterceptor.doFilter()-----------开始拦截器了....
MySecurityMetadataSource:getAttributes()---------------请求地址为:/index.jsp
------------MyFilterSecurityInterceptor.doFilter()-----------拦截器该方法结束了....
------------MyFilterSecurityInterceptor.doFilter()-----------开始拦截器了....
MySecurityMetadataSource:getAttributes()---------------请求地址为:/user/admin
------------MyFilterSecurityInterceptor.doFilter()-----------拦截器该方法结束了....
---------MyUserDetailServiceImpl:loadUserByUsername------正在加载用户名和密码,用户名为:test_u
--------------------------验证失败,重新登录中.......


博主,你好。感谢你的博文,让我对spring sec了解到了不少。下面是我在调试过程中遇到的一些问题:
1、上面这段控制台提示分别是我访问主页,返回了登录主界面,这没问题。
2、访问/user/admin(还没有登录),浏览器显示了admin.jsp的页面内容,这里是否起到了拦截的作用?
3、登录测试,返回如上结果。

能否发一份带测试数据的数据库给我?谢谢。
mail:cataf@foxmail.com
14 楼 keaco204 2013-12-10  
楼主,你好,我在用你上面给的 <http auto-config="true" access-denied-page="/user/denied"> 定义没有权限时403的页面,出现了下面的异常,A universal match pattern ('/**') is defined  before other patterns in the filter chain, causing them to be ignored. Please check the ordering in your <security:http> namespace or FilterChainProxy bean configuration,如果没有配置403页面就没问题。还有一个就是想问一下hasIpAddress()这个,括号里那个掩码是怎么解释的,http://forum.spring.io/forum/spring-projects/security/95303-how-to-use-hasipaddress,这个网址上的问题跟我现在的问题差不多,但是我看了一下那个回答,还是 不太明白,求教
13 楼 zqb666kkk 2013-12-05  
数据库 文件发我一份 谢谢 6637152@qq.com
12 楼 aokunsang 2013-08-05  
windy0605 写道
lz 关于你说的第二种 将所有信息配置在数据库中,无法拦截URl

不会啊,我现在的项目中使用的就是将所有信息配置在数据库中存储的,能够拦截没问题。
11 楼 windy0605 2013-08-04  
lz 关于你说的第二种 将所有信息配置在数据库中,无法拦截URl
10 楼 lijianfeng007 2013-06-14  
你好,楼主,可以发一份数据库给我吗?谢谢。我邮箱是549071257@qq.com

相关推荐

Global site tag (gtag.js) - Google Analytics