SpringSecurity 自定义用户 角色 资源权限控制
1 package com.joyen.learning.security; 2 3 import java.sql.ResultSet; 4 import java.sql.SQLException; 5 import java.util.List; 6 7 import org.springframework.context.support.MessageSourceAccessor; 8 import org.springframework.dao.DataAccessException; 9 import org.springframework.jdbc.core.RowMapper; 10 import org.springframework.jdbc.core.support.JdbcDaoSupport; 11 import org.springframework.security.core.GrantedAuthority; 12 import org.springframework.security.core.SpringSecurityMessageSource; 13 import org.springframework.security.core.authority.AuthorityUtils; 14 import org.springframework.security.core.authority.GrantedAuthorityImpl; 15 import org.springframework.security.core.userdetails.UserDetails; 16 import org.springframework.security.core.userdetails.UserDetailsService; 17 import org.springframework.security.core.userdetails.UsernameNotFoundException; 18 19 /** 20 * 在这个类中,从数据库中读入用户的密码,角色信息,是否锁定,账号是否过期等 21 * @author fwj 22 * 23 */ 24 public class MyUserDetailService extends JdbcDaoSupport implements UserDetailsService { 25 26 27 private String authoritiesByUsernameQuery; 28 private String usersByUsernameQuery; 29 30 protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); 31 32 public UserDetails loadUserByUsername(String username) 33 throws UsernameNotFoundException, DataAccessException { 34 List<MyUser> users = loadUsersByUsername(username); 35 36 if (users==null || users.size() == 0) { 37 logger.debug("Query returned no results for user '" + username + "'"); 38 39 throw new UsernameNotFoundException( 40 messages.getMessage("JdbcDaoImpl.notFound", new Object[]{username}, "Username {0} not found"), username); 41 } 42 43 MyUser user = users.get(0); 44 List<GrantedAuthority> dbAuths = loadUserAuthorities(user.getUsername()); 45 46 if (dbAuths == null || dbAuths.size() == 0) { 47 logger.debug("User '" + username + "' has no authorities and will be treated as 'not found'"); 48 49 throw new UsernameNotFoundException( 50 messages.getMessage("JdbcDaoImpl.noAuthority", 51 new Object[] {username}, "User {0} has no GrantedAuthority"), username); 52 } 53 54 return createUserDetails(username,user,dbAuths); 55 56 } 57 58 protected List<MyUser> loadUsersByUsername(String username) { 59 60 return getJdbcTemplate().query(usersByUsernameQuery, new String[] {username}, new RowMapper<MyUser>() { 61 public MyUser mapRow(ResultSet rs, int rowNum) throws SQLException { 62 String username = rs.getString(1); 63 String password = rs.getString(2); 64 String email = rs.getString(3); 65 boolean enabled = rs.getBoolean(4); 66 return new MyUser(username, password, email, enabled, true, true, true, AuthorityUtils.NO_AUTHORITIES); 67 } 68 69 }); 70 } 71 72 protected List<GrantedAuthority> loadUserAuthorities(String username) { 73 return getJdbcTemplate().query(authoritiesByUsernameQuery, new String[] {username}, new RowMapper<GrantedAuthority>() { 74 public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException { 75 String roleName = rs.getString(2); 76 GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName); 77 78 return authority; 79 } 80 }); 81 } 82 83 protected UserDetails createUserDetails(String username, MyUser userFromUserQuery, 84 List<GrantedAuthority> combinedAuthorities) { 85 String returnUsername = userFromUserQuery.getUsername(); 86 87 return new MyUser(returnUsername, userFromUserQuery.getPassword(), userFromUserQuery.getEmail(), userFromUserQuery.isEnabled(), 88 true, true, true, combinedAuthorities); 89 } 90 91 public String getAuthoritiesByUsernameQuery() { 92 return authoritiesByUsernameQuery; 93 } 94 95 public void setAuthoritiesByUsernameQuery(String authoritiesByUsernameQuery) { 96 this.authoritiesByUsernameQuery = authoritiesByUsernameQuery; 97 } 98 99 public String getUsersByUsernameQuery() { 100 return usersByUsernameQuery; 101 } 102 103 public void setUsersByUsernameQuery(String usersByUsernameQuery) { 104 this.usersByUsernameQuery = usersByUsernameQuery; 105 } 106 107 108 109 }
1 package com.joyen.learning.security; 2 3 import java.util.Collection; 4 5 import org.springframework.security.core.GrantedAuthority; 6 import org.springframework.security.core.userdetails.User; 7 8 public class MyUser extends User { 9 10 /** 11 * 12 */ 13 private static final long serialVersionUID = 1L; 14 private final String email; 15 16 public MyUser(String username, String password, String email, boolean enabled, 17 boolean accountNonExpired, boolean credentialsNonExpired, 18 boolean accountNonLocked, 19 Collection<? extends GrantedAuthority> authorities) { 20 21 super(username, password, enabled, accountNonExpired, credentialsNonExpired, 22 accountNonLocked, authorities); 23 // TODO Auto-generated constructor stub 24 this.email = email; 25 } 26 27 public String getEmail() { 28 return email; 29 } 30 31 }
1 package com.joyen.learning.security; 2 3 import java.io.IOException; 4 5 import javax.servlet.Filter; 6 import javax.servlet.FilterChain; 7 import javax.servlet.FilterConfig; 8 import javax.servlet.ServletException; 9 import javax.servlet.ServletRequest; 10 import javax.servlet.ServletResponse; 11 12 import org.springframework.security.access.SecurityMetadataSource; 13 import org.springframework.security.access.intercept.AbstractSecurityInterceptor; 14 import org.springframework.security.access.intercept.InterceptorStatusToken; 15 import org.springframework.security.web.FilterInvocation; 16 import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; 17 18 public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter { 19 20 21 private FilterInvocationSecurityMetadataSource securityMetadataSource; 22 23 public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() { 24 return securityMetadataSource; 25 } 26 27 public void setSecurityMetadataSource( 28 FilterInvocationSecurityMetadataSource securityMetadataSource) { 29 this.securityMetadataSource = securityMetadataSource; 30 } 31 32 @Override 33 public Class<? extends Object> getSecureObjectClass() { 34 return FilterInvocation.class; 35 } 36 37 @Override 38 public SecurityMetadataSource obtainSecurityMetadataSource() { 39 return this.securityMetadataSource; 40 } 41 42 public void destroy() { 43 // TODO Auto-generated method stub 44 45 } 46 47 public void doFilter(ServletRequest request, ServletResponse response, 48 FilterChain chain) throws IOException, ServletException { 49 FilterInvocation fi = new FilterInvocation(request, response, chain); 50 invoke(fi); 51 52 } 53 54 public void init(FilterConfig arg0) throws ServletException { 55 // TODO Auto-generated method stub 56 57 } 58 59 public void invoke(FilterInvocation fi) throws IOException, ServletException { 60 InterceptorStatusToken token = super.beforeInvocation(fi); 61 try { 62 fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); 63 } finally { 64 super.afterInvocation(token, null); 65 } 66 } 67 68 }
1 package com.joyen.learning.security; 2 3 import java.util.Collection; 4 import java.util.Iterator; 5 6 import org.springframework.security.access.AccessDecisionManager; 7 import org.springframework.security.access.AccessDeniedException; 8 import org.springframework.security.access.ConfigAttribute; 9 import org.springframework.security.access.SecurityConfig; 10 import org.springframework.security.authentication.InsufficientAuthenticationException; 11 import org.springframework.security.core.Authentication; 12 import org.springframework.security.core.GrantedAuthority; 13 import org.springframework.security.web.FilterInvocation; 14 15 /** 16 * 决策类 17 * 如果不存在对该资源的定义,直接放行;否则,如果找到正确的角色,即认为拥有权限,并放行 18 * @author fwj 19 * 20 */ 21 public class MyAccessDecisionManager implements AccessDecisionManager { 22 23 public void decide(Authentication authentication, Object object, 24 Collection<ConfigAttribute> configAttributes) 25 throws AccessDeniedException, InsufficientAuthenticationException { 26 27 if(configAttributes == null){ 28 return ; 29 } 30 31 FilterInvocation fi = (FilterInvocation)object; 32 System.out.println("=============request url==========="+fi.getRequestUrl()); //object is a URL. 33 Iterator<ConfigAttribute> ite=configAttributes.iterator(); 34 while(ite.hasNext()){ 35 ConfigAttribute ca=ite.next(); 36 String needRole=((SecurityConfig)ca).getAttribute(); 37 for(GrantedAuthority ga:authentication.getAuthorities()){ 38 if(needRole.equals(ga.getAuthority())){ //ga is user's role. 39 return; 40 } 41 } 42 } 43 throw new AccessDeniedException("no right"); 44 45 } 46 47 public boolean supports(ConfigAttribute attribute) { 48 // TODO Auto-generated method stub 49 return true; 50 } 51 52 public boolean supports(Class<?> clazz) { 53 // TODO Auto-generated method stub 54 return true; 55 } 56 57 }
1 package com.joyen.learning.security; 2 3 import java.sql.ResultSet; 4 import java.sql.SQLException; 5 import java.util.ArrayList; 6 import java.util.Collection; 7 import java.util.HashMap; 8 import java.util.Iterator; 9 import java.util.List; 10 import java.util.Map; 11 12 import javax.sql.DataSource; 13 14 import org.springframework.jdbc.core.RowMapper; 15 import org.springframework.jdbc.core.support.JdbcDaoSupport; 16 import org.springframework.security.access.ConfigAttribute; 17 import org.springframework.security.access.SecurityConfig; 18 import org.springframework.security.web.FilterInvocation; 19 import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; 20 import org.springframework.security.web.util.AntUrlPathMatcher; 21 import org.springframework.security.web.util.UrlMatcher; 22 23 /** 24 * 25 * 此类在初始化时,应该取到所有资源及其对应角色的定义 26 * 27 * @author fuwenjun 28 * 29 */ 30 public class MyInvocationSecurityMetadataSource extends JdbcDaoSupport implements 31 FilterInvocationSecurityMetadataSource { 32 33 private String resourceQuery; 34 private UrlMatcher urlMatcher = new AntUrlPathMatcher(); 35 private static Map<String, Collection<ConfigAttribute>> resourceMap = null; 36 37 public MyInvocationSecurityMetadataSource(DataSource dataSource,String resourceQuery) { 38 this.setDataSource(dataSource); 39 this.resourceQuery = resourceQuery; 40 this.loadResourceDefine(); 41 } 42 43 private void loadResourceDefine(){ 44 resourceMap = new HashMap<String, Collection<ConfigAttribute>>(); 45 List<ResourceRole> list = getJdbcTemplate().query(resourceQuery, new RowMapper<ResourceRole>() { 46 public ResourceRole mapRow(ResultSet rs, int rowNum) throws SQLException { 47 String url = rs.getString(1); 48 String role = rs.getString(2); 49 return new ResourceRole(url, role); 50 } 51 }); 52 ConfigAttribute ca = null; 53 Collection<ConfigAttribute> cca = null; 54 for (ResourceRole resourceRole : list) { 55 if(resourceMap.containsKey(resourceRole.getUrl())){ 56 ca = new SecurityConfig(resourceRole.getRole()); 57 resourceMap.get(resourceRole.getUrl()).add(ca); 58 }else{ 59 ca = new SecurityConfig(resourceRole.getRole()); 60 cca = new ArrayList<ConfigAttribute>();//首次创建一个新的configattribute集合 61 cca.add(ca); 62 resourceMap.put(resourceRole.getUrl(), cca); 63 } 64 } 65 66 } 67 68 public Collection<ConfigAttribute> getAttributes(Object object) 69 throws IllegalArgumentException { 70 String url = ((FilterInvocation)object).getRequestUrl(); 71 Iterator<String> ite = resourceMap.keySet().iterator(); 72 while (ite.hasNext()) { 73 String resURL = ite.next(); 74 if (urlMatcher.pathMatchesUrl(url, resURL)) { 75 return resourceMap.get(resURL); 76 } 77 } 78 return null; 79 } 80 81 public Collection<ConfigAttribute> getAllConfigAttributes() { 82 // TODO Auto-generated method stub 83 return null; 84 } 85 86 public boolean supports(Class<?> clazz) { 87 // TODO Auto-generated method stub 88 return true; 89 } 90 91 }
1 package com.joyen.learning.security; 2 3 public class ResourceRole { 4 5 private String url; 6 private String role; 7 8 public ResourceRole(String url, String role) { 9 this.url = url; 10 this.role = role; 11 } 12 13 public String getUrl() { 14 return url; 15 } 16 17 public String getRole() { 18 return role; 19 } 20 }
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.0.xsd"> 9 10 <http access-denied-page="/403" auto-config="false"><!-- 当访问被拒绝时,会转到403.jsp --> 11 <intercept-url pattern="/login" filters="none" /> 12 <form-login login-page="/login" 13 authentication-failure-url="/login?error=true" 14 default-target-url="/index"/> 15 <logout logout-success-url="/login" /> 16 <!-- 增加一个filter,这点与Acegi是不一样的,不能修改默认的filter了,这个filter位于FILTER_SECURITY_INTERCEPTOR之前 --> 17 <custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="myFilter" /> 18 </http> 19 20 <!-- 认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 --> 21 <authentication-manager alias="authenticationManager"> 22 <authentication-provider 23 user-service-ref="myUserDetailService"> 24 <!-- 如果用户的密码采用加密的话,可以加点“盐” 25 <password-encoder hash="md5" /> 26 --> 27 </authentication-provider> 28 </authentication-manager> 29 30 31 <!-- 一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性,我们的所有控制将在这三个类中实现,解释详见具体配置 --> 32 <beans:bean id="myFilter" class="com.joyen.learning.security.MyFilterSecurityInterceptor"> 33 <beans:property name="authenticationManager" 34 ref="authenticationManager" /> 35 <beans:property name="accessDecisionManager" 36 ref="myAccessDecisionManagerBean" /> 37 <beans:property name="securityMetadataSource" 38 ref="securityMetadataSource" /> 39 </beans:bean> 40 41 42 <beans:bean id="myUserDetailService" 43 class="com.joyen.learning.security.MyUserDetailService"> 44 <beans:property name="dataSource" ref="dataSource"></beans:property> 45 <beans:property name="usersByUsernameQuery" value="select username,password,email,enabled from user where username = ?"></beans:property> 46 <beans:property name="authoritiesByUsernameQuery" value="SELECT u.username,r.name 47 FROM user u,roleuser ru, role r 48 WHERE u.id = ru.userid 49 AND ru.roleid = r.id 50 AND u.username = ?"></beans:property> 51 </beans:bean> 52 53 <!-- 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 --> 54 <beans:bean id="myAccessDecisionManagerBean" 55 class="com.joyen.learning.security.MyAccessDecisionManager"> 56 </beans:bean> 57 58 <!-- 资源源数据定义,即定义某一资源可以被哪些角色访问 --> 59 <beans:bean id="securityMetadataSource" 60 class="com.joyen.learning.security.MyInvocationSecurityMetadataSource"> 61 <beans:constructor-arg ref="dataSource"></beans:constructor-arg> 62 <beans:constructor-arg type="java.lang.String" value="select rce.url, r.name from role r inner join roleresource rrce on r.id = rrce.roleid inner join resource rce on rrce.resourceid = rce.id"></beans:constructor-arg> 63 </beans:bean> 64 </beans:beans>