1. 程式人生 > >spring boot 中spring security使用資料庫儲存許可權

spring boot 中spring security使用資料庫儲存許可權

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;
    }

}