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安全设计的配置的更多相关文章

  1. spring boot web相关配置

    spring boot集成了servlet容器,当我们在pom文件中增加spring-boot-starter-web的maven依赖时,不做任何web相关的配置便能提供web服务,这还得归于spri ...

  2. 初识Spring Boot框架(二)之DIY一个Spring Boot的自动配置

    在上篇博客初识Spring Boot框架中我们初步见识了SpringBoot的方便之处,很多小伙伴可能也会好奇这个Spring Boot是怎么实现自动配置的,那么今天我就带小伙伴我们自己来实现一个简单 ...

  3. Spring Boot 2.0 配置图文教程

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 本章内容 自定义属性快速入门 外化配置 自动配置 自定义创建 ...

  4. Spring boot 的自动配置

    Xml 配置文件 日志 Spring Boot对各种日志框架都做了支持,我们可以通过配置来修改默认的日志的配置: #设置日志级别 logging.level.org.springframework=D ...

  5. spring boot多数据源配置(mysql,redis,mongodb)实战

    使用Spring Boot Starter提升效率 虽然不同的starter实现起来各有差异,但是他们基本上都会使用到两个相同的内容:ConfigurationProperties和AutoConfi ...

  6. Spring Boot SSL [https]配置例子

    前言 本文主要介绍Spring Boot HTTPS相关配置,基于自签证书实现: 通过本例子,同样可以了解创建SSL数字证书的过程: 本文概述 Spring boot HTTPS 配置 server. ...

  7. spring boot 系列之六:深入理解spring boot的自动配置

    我们知道,spring boot自动配置功能可以根据不同情况来决定spring配置应该用哪个,不应该用哪个,举个例子: Spring的JdbcTemplate是不是在Classpath里面?如果是,并 ...

  8. 转-spring boot web相关配置

    spring boot web相关配置 80436 spring boot集成了servlet容器,当我们在pom文件中增加spring-boot-starter-web的maven依赖时,不做任何w ...

  9. spring boot日志管理配置

    spring Boot在所有内部日志中使用Commons Logging,但是默认配置也提供了对常用日志的支持,如:Java Util Logging,Log4J,Log4J2和Logback.每种L ...

随机推荐

  1. java中instanceof的基本使用

    java中的instanceof运算符是用于判断对象是否是指定类或这个指定类的子类的一个实例,返回值是布尔类型. 语法: boolean result = object instanceof clas ...

  2. jdk1.8-stack 栈源码分析

    一:类的继承关系 UML图: 类的继承关系: )) { ) { ) );} 分析:栈的peek()方法,其实就是返回栈顶部的元素值,即数组末尾元素值. peek()方法只是返回下标的值,但并不删除该元 ...

  3. 利用js代码自动删除稿件的普通弹幕

    事情的起因是在b站投稿了一个高级弹幕测试的视频(av9940487),但是由于b站的弹幕池机制是新的弹幕顶掉旧的弹幕,所以导致一些人发的高级弹幕很快就被顶掉了. 所以就想着写个脚本来自动删除属性为普通 ...

  4. A = min(1, max(0, A))

    Crop A into [0, 1]:

  5. 应用安全 - 工具 - Jmeter - 漏洞 - 汇总

    CVE-2018-1297 Date 类型 rmi 反序列化导致远程代码执行 影响范围

  6. C学习笔记-结构体

    结构体的定义和初始化 结构体是简单数据类型的综合 struct man { char name[100]; int age; }; struct man m = { "tom", ...

  7. 关于Typescript - HTMLElement上使用append / prepend函数的问题

    因最近在做浏览器打印界面水印的问题,用到后台动态创建标签,样式的处理用到了append,prend函数,Angular build打包的时候却抛出了异常↓ ERROR in src/app/route ...

  8. SpringBoot 使用Mybatis操作mysql示例

    1.准备数据库 创建数据库 create databases baodanjia; 创建帐号 create user 'baodanjia'@'%' identified by '123456' gr ...

  9. Replace Words

    In English, we have a concept called root, which can be followed by some other words to form another ...

  10. CF450A 【Jzzhu and Children】

    普通的模拟题这题用一个队列容器来模拟队列元素是pair类型的,first用来存每个小朋友想要的糖数,second用来存小朋友的序号,然后开始模拟,模拟出口是当队列迟到等于1时就输出当前队列里小朋友的序 ...