spring security 登入、許可權管理配置
阿新 • • 發佈:2019-01-27
登入流程
1)容器啟動(MySecurityMetadataSource:loadResourceDefine載入系統資源與許可權列表)
2)使用者發出請求
3)過濾器攔截(MySecurityFilter:doFilter)
4)取得請求資源所需許可權(MySecurityMetadataSource:getAttributes)
5)匹配使用者擁有許可權和請求許可權(MyAccessDecisionManager:decide),如果使用者沒有相應的許可權,
執行第6步,否則執行第7步。
6)登入
7)驗證並授權(MyUserDetailServiceImpl:loadUserByUsername)
1、web.xml中加入過濾器
2、新建spring-security.xml檔案<!-- SpringSecurity 核心過濾器配置 --> <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>
<?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.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <!-- entry-point-ref 配置自定義登入 --> <http use-expressions="true" entry-point-ref="authenticationProcessingFilterEntryPoint"> <!-- 登出配置 --> <logout logout-url="/j_spring_security_logout" logout-success-url="/login" /> <access-denied-handler error-page="/noPower" /> <!-- 過濾不被攔截的請求 --> <intercept-url pattern="/login*" access="permitAll" /> <intercept-url pattern="/resources/**" access="permitAll" /> <!-- 只有許可權才能訪問的請求 --> <intercept-url pattern="/admin/**" access="isAuthenticated()" /> <custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER" /> <custom-filter ref="securityFilter" before="FILTER_SECURITY_INTERCEPTOR" /> </http> <beans:bean id="loginFilter" class="cn.com.abel.test.service.security.MyUsernamePasswordAuthenticationFilter"> <!-- 登入提交處理 --> <beans:property name="filterProcessesUrl" value="/j_spring_security_check"></beans:property> <!-- 登入成功跳轉 --> <beans:property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler"></beans:property> <!-- 設定登入失敗的網址 --> <beans:property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"></beans:property> <!-- 使用者擁有許可權 --> <beans:property name="authenticationManager" ref="myAuthenticationManager"></beans:property> </beans:bean> <beans:bean id="loginLogAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"> <beans:property name="defaultTargetUrl" value="/admin/index"></beans:property> </beans:bean> <beans:bean id="simpleUrlAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"> <beans:property name="defaultFailureUrl" value="/login"></beans:property> </beans:bean> <authentication-manager alias="myAuthenticationManager"> <authentication-provider user-service-ref="myUserDetailServiceImpl"> <password-encoder ref="encoder" /> </authentication-provider> </authentication-manager> <beans:bean id="myUserDetailServiceImpl" class="cn.com.abel.test.service.security.AdminUserDetailServiceImpl"> </beans:bean> <beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"> <beans:property name="loginFormUrl" value="/login"></beans:property> </beans:bean> <!-- 認證過濾器 --> <beans:bean id="securityFilter" class="cn.com.abel.test.service.security.MySecurityFilter"> <!-- 使用者擁有的角色 --> <beans:property name="authenticationManager" ref="myAuthenticationManager" /> <!-- 使用者是否擁有所請求資源的許可權 --> <beans:property name="accessDecisionManager" ref="myAccessDecisionManager" /> <!-- 資源與角色的對應關係 --> <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource" /> <!-- <beans:property name="rejectPublicInvocations" value="true"/> --> </beans:bean> <beans:bean id="myAccessDecisionManager" class="myAccessDecisionManager"></beans:bean> <beans:bean id="mySecurityMetadataSource" class="cn.com.abel.test.service.security.MySecurityMetadataSource"> <beans:constructor-arg> <beans:ref bean="resourceService" /> </beans:constructor-arg> </beans:bean> <beans:bean id="encoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"></beans:bean> </beans:beans>
3、MyUsernamePasswordAuthenticationFilter.java
package cn.com.abel.test.service.security;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
// //登入驗證碼,如需要開啟把下面註釋去掉則可
// String authCode = StringUtils.defaultString(request.getParameter("authCode"));
// if(!AdwImageCaptchaServlet.validateResponse(request, authCode)){
// throw new AuthenticationServiceException("validCode.auth.fail");
// }
return this.getAuthenticationManager().authenticate(authRequest);
}
}
4、MySecurityFilter.java
package cn.com.abel.test.service.security;
import java.io.IOException;
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.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 {
//與applicationContext-security.xml裡的myFilter的屬性securityMetadataSource對應,
//其他的兩個元件,已經在AbstractSecurityInterceptor定義
private FilterInvocationSecurityMetadataSource securityMetadataSource;
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
private void invoke(FilterInvocation fi) throws IOException, ServletException {
// object為FilterInvocation物件
//1.獲取請求資源的許可權
//執行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()
//System.out.println("使用者傳送請求! ");
InterceptorStatusToken token = null;
token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return securityMetadataSource;
}
public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
}
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public Class<? extends Object> getSecureObjectClass() {
//下面的MyAccessDecisionManager的supports方面必須放回true,否則會提醒型別錯誤
return FilterInvocation.class;
}
}
5、AdminUserDetailServiceImpl.java
package cn.com.abel.test.service.security;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import cn.com.abel.test.model.RoleModel;
import cn.com.abel.test.model.MemberModel;
import cn.com.abel.test.service.RoleService;
import cn.com.abel.test.service.MemberService;
public class AdminUserDetailServiceImpl implements UserDetailsService {
@Autowired
private MemberService memberService;
@Autowired
RoleService roleService;
//登入驗證
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
MemberModel member = memberService.getUserDetailsByUserName(username);
if(member==null){
throw new UsernameNotFoundException("member "+username +" not found.");
}
Set<GrantedAuthority> grantedAuths = obtionGrantedAuthorities(member);
//封裝成spring security的user
User userdetail = new User(user.getUserName(), user.getPassword(),
true, // 賬號狀態 0 表示停用 1表示啟用
true, true, true, grantedAuths // 使用者的許可權
);
return userdetail;
}
//取得使用者的許可權
private Set<GrantedAuthority> obtionGrantedAuthorities(MemberModel member) {
Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
List<RoleModel> roles = roleService.getRoleByUser(member)<span style="font-family:Arial, Helvetica, sans-serif;">;</span>
if(roles!=null){
for(RoleModel role : roles) {
authSet.add(new SimpleGrantedAuthority(role.getRoleCode().trim()));
}
}
return authSet;
}
}
6、MyAccessDecisionManager.java
package cn.com.abel.test.service.security;
import java.util.Collection;
import java.util.Iterator;
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;
public class MyAccessDecisionManager implements AccessDecisionManager {
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
if(configAttributes == null) {
return;
}
//所請求的資源擁有的許可權(一個資源對多個許可權)
Iterator<ConfigAttribute> iterator = configAttributes.iterator();
while(iterator.hasNext()) {
ConfigAttribute configAttribute = iterator.next();
//訪問所請求資源所需要的許可權
String needPermission = configAttribute.getAttribute();
System.out.println("needPermission is " + needPermission);
//使用者所擁有的許可權authentication
for(GrantedAuthority ga : authentication.getAuthorities()) {
if(needPermission.equals(ga.getAuthority())) {
return;
}
}
}
//沒有許可權讓我們去捕捉
throw new AccessDeniedException(" 沒有許可權訪問!");
}
public boolean supports(ConfigAttribute attribute) {
// TODO Auto-generated method stub
return true;
}
public boolean supports(Class<?> clazz) {
// TODO Auto-generated method stub
return true;
}
}
7、MySecurityMetadataSource.java
package cn.com.abel.test.service.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
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.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import cn.com.abel.test.model.RoleModel;
import cn.com.abel.test.service.ResourceService;
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource,InitializingBean {
private static final String AUTH_NO_ROLE =" __AUTH_NO_ROLE__";
private ResourceService resourceService;
public MySecurityMetadataSource(ResourceService resourceService) {
this.resourceService = resourceService;
}
private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public boolean supports(Class<?> clazz) {
return true;
}
private void loadResourceDefine() {
if(resourceMap == null) {
resourceMap = new ConcurrentHashMap<String, Collection<ConfigAttribute>>();
}else{
resourceMap.clear();
}
Map<String,List<RoleModel>> resourceRoleMap = resourceService.getAllResourceRole();
for (Entry<String,List<RoleModel>> entry : resourceRoleMap.entrySet()) {
String url = entry.getKey();
List<RoleModel> values = entry.getValue();
Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
for(RoleModel secRoleModel : values){
ConfigAttribute configAttribute = new SecurityConfig(StringUtils.defaultString(secRoleModel.getRoleCode(),AUTH_NO_ROLE));
configAttributes.add(configAttribute);
}
resourceMap.put(url, configAttributes);
}
}
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
TreeMap<String, Collection<ConfigAttribute>> attrMap = new TreeMap<String, Collection<ConfigAttribute>>(resourceMap);
Iterator<String> ite = attrMap.keySet().iterator();
RequestMatcher urlMatcher = null;
Collection<ConfigAttribute> attrSet = new HashSet<ConfigAttribute>();
//match all of /admin/** a/b/**
while (ite.hasNext()) {
String resURL = ite.next();
urlMatcher = new AntPathRequestMatcher(resURL);
if (urlMatcher.matches(request)||StringUtils.equals(request.getRequestURI(),resURL)) {
attrSet.addAll(attrMap.get(resURL));
}
}
if(!attrSet.isEmpty()){
return attrSet;
}
return null;
}
@Override
public void afterPropertiesSet() throws Exception {
loadResourceDefine() ;
}
}
8、ResourceService.java
此類是為從資料庫獲取系統中的資源所屬的角色,根據自己的資料表自行編寫。
package cn.com.abel.test.service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cn.com.abel.test.mapper.ResourceModelMapper;
import cn.com.abel.test.mapper.RoleModelMapper;
import cn.com.abel.test.mapper.RoleResourcetModelMapper;
import cn.com.abel.test.model.ResourceModel;
import cn.com.abel.test.model.ResourceModelCriteria;
import cn.com.abel.test.model.RoleModel;
import cn.com.abel.test.model.RoleModelCriteria;
import cn.com.abel.test.model.RoleResourcetModel;
import cn.com.abel.test.model.RoleResourcetModelCriteria;
@Service
public class ResourceService {
@Autowired
ResourceModelMapper resourceModelMapper;
@Autowired
RoleModelMapper roleMapper;
@Autowired
RoleResourcetModelMapper roleResMapper;
/**
* 獲取各個資源(url)對應的角色
* @return
*/
public Map<String,List<RoleModel>> getAllResourceRole(){
Map<String,List<RoleModel>> resultMap = new HashMap<String,List<RoleModel>>();
ResourceModelCriteria secResourceModelExample = new ResourceModelCriteria();
List<ResourceModel> resourceList = resourceModelMapper.selectByExample(secResourceModelExample);
if(CollectionUtils.isNotEmpty(resourceList)){
for(ResourceModel secResourceModel : resourceList){
RoleModelCriteria roleCriteria = new RoleModelCriteria();
roleCriteria.createCriteria().andIdIn(getRoleIdsByResourceId(secResourceModel.getId()));
List<RoleModel> roleList = roleMapper.selectByExample(roleCriteria);
resultMap.put(secResourceModel.getValue(), roleList);
}
}
return resultMap;
}
public List<Integer> getRoleIdsByResourceId(Integer resourceId){
List<Integer> roleIds = new ArrayList<Integer>();
RoleResourcetModelCriteria criteria = new RoleResourcetModelCriteria();
criteria.createCriteria().andResourceIdEqualTo(resourceId);
List<RoleResourcetModel> list = roleResMapper.selectByExample(criteria);
if(CollectionUtils.isNotEmpty(list)){
for(RoleResourcetModel model : list){
roleIds.add(model.getRoleId());
}
}
HashSet<Integer> h = new HashSet<Integer>(roleIds);
roleIds.clear();
roleIds.addAll(h);
return roleIds;
}
}
最後附上資料表的SQL:
CREATE TABLE `auth_resource` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NULL DEFAULT NULL COMMENT '資源名稱',
`value` VARCHAR(100) NULL DEFAULT NULL COMMENT '資源值',
`summary` VARCHAR(1000) NULL DEFAULT NULL COMMENT '資源描述',
`updated_time` DATETIME NULL DEFAULT NULL,
`updated_user` VARCHAR(100) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
)
COMMENT='資源訪問表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
CREATE TABLE `auth_role` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`role_name` VARCHAR(100) NULL DEFAULT NULL COMMENT '角色名稱',
`role_code` VARCHAR(100) NULL DEFAULT NULL COMMENT '角色程式碼',
`updated_time` DATETIME NULL DEFAULT NULL,
`updated_user` VARCHAR(100) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
)
COMMENT='角色表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
CREATE TABLE `role_resource` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`role_id` INT(11) NOT NULL,
`resource_id` INT(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `role_id_resource_id` (`role_id`, `resource_id`)
)
COMMENT='資源角色關聯表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
CREATE TABLE `member` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`user_name` VARCHAR(100) NULL DEFAULT NULL,
`nick` VARCHAR(100) NULL DEFAULT NULL,
`password` VARCHAR(100) NULL DEFAULT NULL,
`sex` INT(11) NULL DEFAULT NULL,
`birthday` DATE NULL DEFAULT NULL,
`mobile` VARCHAR(50) NULL DEFAULT NULL,
`email` VARCHAR(50) NULL DEFAULT NULL,
`address` VARCHAR(512) NULL DEFAULT NULL,
`regip` VARCHAR(100) NULL DEFAULT NULL,
`created_time` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`)
)
COMMENT='使用者表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=2;
CREATE TABLE `member_role` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`member_id` INT(11) NOT NULL,
`role_id` INT(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `member_id_role_id` (`member_id`, `role_id`)
)
COMMENT='使用者角色關聯表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
完整程式碼下載(包含資料庫):http://download.csdn.net/download/rongku/9931455