spring boot 中spring security使用資料庫儲存許可權
阿新 • • 發佈:2019-01-23
WebSecurityConfig
package com.maven;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration ;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders .AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter ;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.maven.security.MyFilterSecurityInterceptor;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private DataSource dataSource;
@Autowired
@Qualifier("myUserDetailsService")
private UserDetailsService userDetailsService;
@Autowired
@Qualifier("myAccessDecisionManager")
private AccessDecisionManager accessDecisionManager;
@Autowired
@Qualifier("mySecurityMetadataSource")
private FilterInvocationSecurityMetadataSource securityMetadataSource;
@Autowired
private AuthenticationManagerBuilder authenticationManagerBuilder;
private AuthenticationManager authenticationManager;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//自定義過濾器,把資源配置存放到資料庫中
.addFilterBefore(filter(),FilterSecurityInterceptor.class)
.exceptionHandling()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/home")
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout")
.invalidateHttpSession(true)
.deleteCookies("remember-me")
.and()
.rememberMe()
.tokenRepository(tokenRepository())
.tokenValiditySeconds(1209600)//預設2周
.rememberMeCookieName("remember-me")
.userDetailsService(userDetailsService)
.rememberMeParameter("remember-me");
}
private PersistentTokenRepository tokenRepository() {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
return tokenRepository;
}
// 配置AuthenticationManager
@Bean
public AuthenticationManager authenticationManagerBean() {
authenticationManagerBuilder.authenticationProvider(authenticationProvider());
return authenticationManagerBuilder.getOrBuild();
}
// 獲取AuthenticationManager,避免多次呼叫authenticationManagerBean()
public AuthenticationManager getAuthenticationManager() {
if (this.authenticationManager == null) {
return authenticationManagerBean();
} else {
return this.authenticationManager;
}
}
// 自定義JpaFilterSecurityInterceptor過濾器
public MyFilterSecurityInterceptor filter() throws Exception {
MyFilterSecurityInterceptor filter = new MyFilterSecurityInterceptor();
// 認證管理器,實現使用者認證的入口
filter.setAuthenticationManager(getAuthenticationManager());
// 訪問決策器,決定某個使用者具有的角色,是否有足夠的許可權去訪問某個資源
filter.setAccessDecisionManager(accessDecisionManager);
// 資源源資料定義,即定義某一資源可以被哪些角色訪問
filter.setSecurityMetadataSource(securityMetadataSource);
return filter;
}
// 配置DaoAuthenticationProvider
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
// 設定不隱藏UserNotFoundExceptions
authenticationProvider.setHideUserNotFoundExceptions(false);
// 設定自定義UserDetailsService
authenticationProvider.setUserDetailsService(userDetailsService);
// 設定密碼加密方式
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
authenticationProvider.setPasswordEncoder(passwordEncoder);
return authenticationProvider;
}
}
MyAccessDecisionManager
/**
* copyright by liukai
*/
package com.maven.security;
import java.util.Collection;
import java.util.Iterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
@Service("myAccessDecisionManager")
public class MyAccessDecisionManager implements AccessDecisionManager{
private final static Logger logger = LoggerFactory.getLogger(MyAccessDecisionManager.class);
/* (non-Javadoc)
* @see org.springframework.security.access.AccessDecisionManager#decide(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection)
*/
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
if (configAttributes == null) {
return; //如果訪問資源不需要任何許可權則直接通過
}
//所請求的資源擁有的許可權(一個資源對多個許可權)
Iterator<ConfigAttribute> ite = configAttributes.iterator();
while (ite.hasNext()) {
ConfigAttribute ca = ite.next();
String needRole = ((SecurityConfig) ca).getAttribute();
// ga 為使用者所被賦予的許可權。 needRole 為訪問相應的資源應該具有的許可權。
for (GrantedAuthority ga : authentication.getAuthorities()) {
logger.info("該資源所需要的許可權是:"+needRole +",當前使用者的許可權是:"+ ga.getAuthority());
if (needRole.trim().equals(ga.getAuthority().trim())) {
return;
}
}
}
logger.info("許可權不足,無法訪問!");
throw new AccessDeniedException("不允許訪問!");
}
/* (non-Javadoc)
* @see org.springframework.security.access.AccessDecisionManager#supports(org.springframework.security.access.ConfigAttribute)
*/
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
/* (non-Javadoc)
* @see org.springframework.security.access.AccessDecisionManager#supports(java.lang.Class)
*/
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
MyFilterSecurityInterceptor
/**
* copyright by liukai
*/
package com.maven.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 MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter{
private FilterInvocationSecurityMetadataSource securityMetadataSource;
/**
* @return the securityMetadataSource
*/
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return securityMetadataSource;
}
/**
* @param securityMetadataSource the securityMetadataSource to set
*/
public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
}
/* (non-Javadoc)
* @see org.springframework.security.access.intercept.AbstractSecurityInterceptor#getSecureObjectClass()
*/
@Override
public Class<?> getSecureObjectClass() {
return FilterInvocation.class;
}
/* (non-Javadoc)
* @see org.springframework.security.access.intercept.AbstractSecurityInterceptor#obtainSecurityMetadataSource()
*/
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return securityMetadataSource;
}
/* (non-Javadoc)
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
/* (non-Javadoc)
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
public void invoke(FilterInvocation fi) throws IOException, ServletException {
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
/* (non-Javadoc)
* @see javax.servlet.Filter#destroy()
*/
@Override
public void destroy() {
}
}
MySecurityMetadataSource
/**
* copyright by liukai
*/
package com.maven.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.stereotype.Service;
import org.springframework.util.AntPathMatcher;
import com.maven.auth.service.AuthService;
@Service("mySecurityMetadataSource")
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource{
private final static Logger logger = LoggerFactory.getLogger(MySecurityMetadataSource.class);
private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
public MySecurityMetadataSource(AuthService authService) {
//查詢所有資源路徑和角色
List<Map<String,Object>> resourceRoles = authService.findAllResourceAndRole();
//根據URL組裝許可權Map key為URL,集合為許可權集合
resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
for(Map<String,Object> resourceRole :resourceRoles) {
ConfigAttribute ca = new SecurityConfig((String) resourceRole.get("CODE"));
String url = (String) resourceRole.get("PATH");
if (resourceMap.containsKey(url)) {
Collection<ConfigAttribute> value = resourceMap.get(url);
value.add(ca);
resourceMap.put(url, value);
} else {
Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
atts.add(ca);
resourceMap.put(url, atts);
}
}
}
/* (non-Javadoc)
* @see org.springframework.security.access.SecurityMetadataSource#getAttributes(java.lang.Object)
*/
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
// object 是一個URL,被使用者請求的url。
String url = ((FilterInvocation) object).getRequestUrl();
int firstQuestionMarkIndex = url.indexOf("?");
if (firstQuestionMarkIndex != -1) {
url = url.substring(0, firstQuestionMarkIndex);
}
Iterator<String> ite = resourceMap.keySet().iterator();
AntPathMatcher matcher = new AntPathMatcher();
matcher.setTrimTokens(false);
while (ite.hasNext()) {
String resURL = ite.next();
if (matcher.match(resURL, url)) {
logger.info("資源配置URL為"+resURL+"請求的URL為:"+url);
return resourceMap.get(resURL);
}
}
return null;
}
/* (non-Javadoc)
* @see org.springframework.security.access.SecurityMetadataSource#getAllConfigAttributes()
*/
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
// TODO Auto-generated method stub
return null;
}
/* (non-Javadoc)
* @see org.springframework.security.access.SecurityMetadataSource#supports(java.lang.Class)
*/
@Override
public boolean supports(Class<?> clazz) {
// TODO Auto-generated method stub
return true;
}
}
MyUserDetailsService
package com.maven.security;
import java.util.ArrayList;
import java.util.List;
import javax.transaction.Transactional;
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 org.springframework.stereotype.Service;
import com.maven.auth.dao.UserDao;
import com.maven.auth.entity.Role;
import com.maven.auth.entity.User;
@Transactional
@Service("myUserDetailsService")
public class MyUserDetailsService implements UserDetailsService{
@Autowired
private UserDao userDao;
/* (non-Javadoc)
* @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = this.userDao.findByUsername(username);
if(user==null) {
throw new UsernameNotFoundException("使用者"+username+"不存在!");
}
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
List<Role> roles = user.getRoles();
for(Role role:roles) {
authorities.add(new SimpleGrantedAuthority(role.getCode()));
}
UserDetails userDetails = new org.springframework.security.core.userdetails.User(user.getUsername(),user.getPassword(),authorities);
return userDetails;
}
}