Spring Boot安全设计的配置
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安全设计的配置的更多相关文章
- spring boot web相关配置
spring boot集成了servlet容器,当我们在pom文件中增加spring-boot-starter-web的maven依赖时,不做任何web相关的配置便能提供web服务,这还得归于spri ...
- 初识Spring Boot框架(二)之DIY一个Spring Boot的自动配置
在上篇博客初识Spring Boot框架中我们初步见识了SpringBoot的方便之处,很多小伙伴可能也会好奇这个Spring Boot是怎么实现自动配置的,那么今天我就带小伙伴我们自己来实现一个简单 ...
- Spring Boot 2.0 配置图文教程
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 本章内容 自定义属性快速入门 外化配置 自动配置 自定义创建 ...
- Spring boot 的自动配置
Xml 配置文件 日志 Spring Boot对各种日志框架都做了支持,我们可以通过配置来修改默认的日志的配置: #设置日志级别 logging.level.org.springframework=D ...
- spring boot多数据源配置(mysql,redis,mongodb)实战
使用Spring Boot Starter提升效率 虽然不同的starter实现起来各有差异,但是他们基本上都会使用到两个相同的内容:ConfigurationProperties和AutoConfi ...
- Spring Boot SSL [https]配置例子
前言 本文主要介绍Spring Boot HTTPS相关配置,基于自签证书实现: 通过本例子,同样可以了解创建SSL数字证书的过程: 本文概述 Spring boot HTTPS 配置 server. ...
- spring boot 系列之六:深入理解spring boot的自动配置
我们知道,spring boot自动配置功能可以根据不同情况来决定spring配置应该用哪个,不应该用哪个,举个例子: Spring的JdbcTemplate是不是在Classpath里面?如果是,并 ...
- 转-spring boot web相关配置
spring boot web相关配置 80436 spring boot集成了servlet容器,当我们在pom文件中增加spring-boot-starter-web的maven依赖时,不做任何w ...
- spring boot日志管理配置
spring Boot在所有内部日志中使用Commons Logging,但是默认配置也提供了对常用日志的支持,如:Java Util Logging,Log4J,Log4J2和Logback.每种L ...
随机推荐
- ns2的第一个tcl脚本
set ns [new Simulator] set tracef [open example1.tr w]$ns trace-all $tracefset namtf [open example1. ...
- 【C/C++】BOOST 线程完全攻略 - 基础篇
C++多线程开发是一个复杂的事情,mfc下提供了CWinThread类,和AfxBeginThread等等函数,但是在使用中会遇到很多麻烦事情,例如线程之间参数传递的问题,我们一般都是把参数new一个 ...
- c++ | size_t
1.size_t的定义 size_t是一种数据相关的无符号类型,它被设计得足够大以便能够内存中任意对象的大小. 在cstddef头文件中定义了size_t类型,这个文件是C标准库stddef.h头文件 ...
- VisualBasic文件与目录管理FileSystem 类
注解 下表列出了涉及 My.Computer.FileSystem 对象的任务示例. 功能 查看 从文本文件读取 如何:读取文本文件 从带分隔符的文本文件中读取 如何:读取逗号分隔的文本文件 从固定宽 ...
- (转载)Nim博弈论
最近补上次参加2019西安邀请赛的题,其中的E题出现了Nim博弈论,今天打算好好看看Nim博弈论,在网上看到这篇总结得超级好的博客,就转载了过来. 转载:https://www.cnblogs.com ...
- [转帖]从Intel和ARM争霸,谈芯片前世今生
从Intel和ARM争霸,谈芯片前世今生 http://www.itpub.net/2019/07/24/2476/ 长文预警, 写的非常好.. 我尽量写得轻松一些,因为其实这个话题很有趣,仔细探究起 ...
- [转帖]etcd 在超大规模数据场景下的性能优化
etcd 在超大规模数据场景下的性能优化 阿里系统软件技术 2019-05-27 09:13:17 本文共5419个字,预计阅读需要14分钟. http://www.itpub.net/2019/ ...
- (public丶private丶protected) 的理解
public(公有):公有的类成员可以在任何地方被访问. protected(受保护):受保护的类成员则可以被其自身以及其子类和父类访问. private(私有):私有的类成员则只能被其定义所在的类访 ...
- SqlServer中插入数据后如何得到主键ID
使用@@IDENTITY 例如:insert into student(name,age) values('fanqi',23) select @@identity 使用 OUTPUT inserte ...
- Binding的Path(路径)
Binding的源可以是控件(一个控件是另一个控件的Source.控件把自己的容器作为Source),把集合作为ItemsControls的Source,把xml作为Tree或者Menu的Source ...