Spring Boot安全設計的配置
阿新 • • 發佈:2017-08-01
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()); @Autowiredprivate 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安全設計的配置