1. 程式人生 > >Spring Boot安全設計的配置

Spring Boot安全設計的配置

reat 攻擊 authorize etl ror tps objectc prot page

  Web應用的安全管理,包括兩個方面:一是用戶身份認證,即用戶登錄的設計;另一方面是用戶的授權,即一個用戶在一個應用系統中能夠執行哪些操作的權限管理。我這裏使用spring-cloud-security進行安全管理。

  首先是依賴配置

    <parent>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-parent</artifactId>
        <version>Brixton.M5</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true
</optional> </dependency> </dependencies>

  安全策略配置

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
@EnableConfigurationProperties(SecuritySettings.class)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    protected Log log = LogFactory.getLog(getClass());
    @Autowired
    
private AuthenticationManager authenticationManager; @Autowired private SecuritySettings settings; @Autowired private CustomUserDetailsService customUserDetailsService; @Autowired @Qualifier("dataSource") private DataSource dataSource; @Override protected void configure(AuthenticationManagerBuilder auth)
throws Exception { auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder()); //remember me auth.eraseCredentials(false); } @Override protected void configure(HttpSecurity http) throws Exception {//setting是自定義的配置參數 http.formLogin().loginPage("/login").permitAll().successHandler(loginSuccessHandler())  //設定一個自定義的的登陸頁面URL .and().authorizeRequests() .antMatchers("/images/**", "/checkcode", "/scripts/**", "/styles/**").permitAll()  //完全允許訪問的一些URL配置 .antMatchers(settings.getPermitall().split(",")).permitAll() .anyRequest().authenticated() .and().csrf().requireCsrfProtectionMatcher(csrfSecurityRequestMatcher())  //跨站請求偽造,這是一個防止跨站請求偽造攻擊的策略配置 .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER) .and().logout().logoutSuccessUrl(settings.getLogoutsuccssurl())  //設定登出成功的鏈接 .and().exceptionHandling().accessDeniedPage(settings.getDeniedpage())   //配置拒絕訪問的提示鏈接 .and().rememberMe().tokenValiditySeconds(86400).tokenRepository(tokenRepository()); //用來記住用戶的登錄狀態,用戶沒執行推出下次打開頁面不用登陸,時效自己設置 } @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public JdbcTokenRepositoryImpl tokenRepository(){ JdbcTokenRepositoryImpl jtr = new JdbcTokenRepositoryImpl(); jtr.setDataSource(dataSource); return jtr; } @Bean public LoginSuccessHandler loginSuccessHandler(){//設置登陸成功處理器 return new LoginSuccessHandler(); } @Bean public CustomFilterSecurityInterceptor customFilter() throws Exception{ CustomFilterSecurityInterceptor customFilter = new CustomFilterSecurityInterceptor(); customFilter.setSecurityMetadataSource(securityMetadataSource());   customFilter.setAccessDecisionManager(accessDecisionManager()); customFilter.setAuthenticationManager(authenticationManager); return customFilter; } @Bean public CustomAccessDecisionManager accessDecisionManager() {// return new CustomAccessDecisionManager(); } @Bean public CustomSecurityMetadataSource securityMetadataSource() { return new CustomSecurityMetadataSource(settings.getUrlroles()); } private CsrfSecurityRequestMatcher csrfSecurityRequestMatcher(){ //加入需要排除阻止CSRF攻擊的鏈表鏈接,鏈接地址中包含/rest字符串的,對其忽略CSRF保護策略 CsrfSecurityRequestMatcher csrfSecurityRequestMatcher = new CsrfSecurityRequestMatcher(); List<String> list = new ArrayList<String>(); list.add("/rest/"); csrfSecurityRequestMatcher.setExecludeUrls(list); return csrfSecurityRequestMatcher; } }

  自定義的securityconfig配置,放在application.yml中

securityconfig:
  logoutsuccssurl: /
  permitall: /rest/**,/bbs**
  deniedpage: /deny
  urlroles: /**/new/** = admin;
            /**/edit/** = admin,editor;
            /**/delete/** = admin

  權限管理規則

@ConfigurationProperties(prefix="securityconfig")
public class SecuritySettings {
    private String logoutsuccssurl = "/logout";
    private String permitall = "/api";
    private String deniedpage = "/deny";
    private String urlroles;

    public String getLogoutsuccssurl() {//定義推出成功的鏈接
        return logoutsuccssurl;
    }

    public void setLogoutsuccssurl(String logoutsuccssurl) {
        this.logoutsuccssurl = logoutsuccssurl;
    }

    public String getPermitall() {//定義允許訪問的URL列表
        return permitall;
    }

    public void setPermitall(String permitall) {
        this.permitall = permitall;
    }

    public String getDeniedpage() {
        return deniedpage;
    }

    public void setDeniedpage(String deniedpage) {//定義拒絕訪問的信息提示鏈接
        this.deniedpage = deniedpage;
    }

    public String getUrlroles() {
        return urlroles;
    }

    public void setUrlroles(String urlroles) {//鏈接地質與角色權限的配置列表
        this.urlroles = urlroles;
    }
}

  防攻擊策略

public class CsrfSecurityRequestMatcher implements RequestMatcher {
    protected Log log = LogFactory.getLog(getClass());
    private Pattern allowedMethods = Pattern
            .compile("^(GET|HEAD|TRACE|OPTIONS)$");
    /**
     * 需要排除的url列表
     */
    private List<String> execludeUrls;

    @Override
    public boolean matches(HttpServletRequest request) {
        if (execludeUrls != null && execludeUrls.size() > 0) {
            String servletPath = request.getServletPath();
            for (String url : execludeUrls) {
                if (servletPath.contains(url)) {
                    log.info("++++"+servletPath);
                    return false;
                }
            }
        }
        return !allowedMethods.matcher(request.getMethod()).matches();
    }

    public List<String> getExecludeUrls() {
        return execludeUrls;
    }

    public void setExecludeUrls(List<String> execludeUrls) {
        this.execludeUrls = execludeUrls;
    }
}
public class CustomAccessDecisionManager implements AccessDecisionManager {
    private static final Logger logger = Logger.getLogger(CustomAccessDecisionManager.class);

    @Override
    public void decide(Authentication authentication, Object object,
                       Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException, InsufficientAuthenticationException {
        if (configAttributes == null) {
            return;
        }

        //config urlroles
        Iterator<ConfigAttribute> iterator = configAttributes.iterator();

        while (iterator.hasNext()) {
            ConfigAttribute configAttribute = iterator.next();
            //need role
            String needRole = configAttribute.getAttribute();
            //user roles
            for (GrantedAuthority ga : authentication.getAuthorities()) {
                if (needRole.equals(ga.getAuthority())) {
                    return;
                }
            }
            logger.info("need role is " + needRole);
        }
        throw new AccessDeniedException("Cannot Access!");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

}
public class CustomFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
    private static final Logger logger = Logger.getLogger(CustomFilterSecurityInterceptor.class);
    private FilterInvocationSecurityMetadataSource securityMetadataSource;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        logger.debug("===="+fi.getRequestUrl());
        invoke(fi);
    }

    public void invoke(FilterInvocation fi) throws IOException, ServletException {
        InterceptorStatusToken token = super.beforeInvocation(fi);
        try {
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } catch (Exception e) {
            logger.error(e.getMessage());
        } finally {
            super.afterInvocation(token, null);
        }
    }

    public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

    @Override
    public Class<? extends Object> getSecureObjectClass() {
        return FilterInvocation.class;
    }

    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

    public void setSecurityMetadataSource(
            FilterInvocationSecurityMetadataSource smSource) {
        this.securityMetadataSource = smSource;
    }

    public void destroy() {
        // TODO Auto-generated method stub

    }

    public void init(FilterConfig arg0) throws ServletException {
        // TODO Auto-generated method stub

    }

}
public class CustomSecurityMetadataSource implements FilterInvocationSecurityMetadataSource{
    private static final Logger logger = Logger.getLogger(CustomSecurityMetadataSource .class);

    private Map<String, Collection<ConfigAttribute>> resourceMap = null;
    private PathMatcher pathMatcher = new AntPathMatcher();

    private String urlroles;

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    public CustomSecurityMetadataSource  (String urlroles) {
        super();
        this.urlroles = urlroles;
        resourceMap = loadResourceMatchAuthority();
    }

    private Map<String, Collection<ConfigAttribute>> loadResourceMatchAuthority() {

        Map<String, Collection<ConfigAttribute>> map = new HashMap<String, Collection<ConfigAttribute>>();

        if(urlroles != null && !urlroles.isEmpty()){
            String[] resouces = urlroles.split(";");
            for(String resource : resouces){
                String[] urls = resource.split("=");
                String[] roles = urls[1].split(",");
                Collection<ConfigAttribute> list = new ArrayList<ConfigAttribute>();
                for(String role : roles){
                    ConfigAttribute config = new SecurityConfig(role.trim());
                    list.add(config);
                }
                //key:url, value:roles
                map.put(urls[0].trim(), list);
            }
        }else{
            logger.error("‘securityconfig.urlroles‘ must be set");
        }

        logger.info("Loaded UrlRoles Resources.");
        return map;

    }

    @Override
    public Collection<ConfigAttribute> getAttributes(Object object)
            throws IllegalArgumentException {
        String url = ((FilterInvocation) object).getRequestUrl();

        logger.debug("request url is  " + url);

       if(resourceMap == null)
            resourceMap = loadResourceMatchAuthority();

        Iterator<String> ite = resourceMap.keySet().iterator();
        while (ite.hasNext()) {
            String resURL = ite.next();
            if (pathMatcher.match(resURL,url)) {
                return resourceMap.get(resURL);
            }
        }
        return resourceMap.get(url);
    }

    public boolean supports(Class<?> clazz) {
        return true;
    }
}

Spring Boot安全設計的配置