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 ...
随机推荐
- Linux 如何上传/下载文件
注: 如果在操作中,提示没有权限请使用" su - "命令来切换当前账号至" root " 账号 一 . 使用 rz / sz 命令 1 . 登陆 Li ...
- PowerShell学习记录
一.简介——连接 Powershell 是运行在windows机器上实现系统和应用程序管理自动化的命令行脚本环境.你可以把它看成是命令行提示符cmd.exe的扩充,不对,应当是颠覆. powershe ...
- hadoop3.0.0部署
配置前先查下JAVA_HOME的位置vim /etc/profile#set java environment JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1. ...
- 小菜鸟之JAVA面试题库1
四次挥手 客户端发送释放连接报文,关闭客户端到服务端的数据传输 服务端收到后,发送确认报文给客户端 服务端发送释放连接报文,关闭服务端到客户端的数据传输 客户端发送一个确认报文给服务端 ------- ...
- 如何获取字符串中某个具体的数值--通过json.load转化成字典形式获取
r=requests.get('http://httpbin.org/get').text print(r) # print(type(r)) # 如果要获取User-Agent的详细数值,需要做JS ...
- Centos 安装Pycharm 并移动到桌面。
版权声明:版权所有.未经同意不得转发,装载 https://blog.csdn.net/limingyue0312/article/details/81805826 1.下载pycharm软件包 网页 ...
- C/C++的几个输入流
C: 1.scanf( ) 存在于<stdio.h>(C++为<cstdio>)中,根据stdin读取数据并根据参数格式进行赋值,以第一个非空格字符(空格字符如:空格,制符表, ...
- 从入门到自闭之Python闭包
闭包 定义:在嵌套函数内,使用(非本层变量)非全局变量就是闭包 闭包必须是内层函数对外层函数的变量(非全局变量)的引用 函数执行完毕后,函数体内的空间自行销毁 def func(): a=1 def ...
- Django基础之模型(models)层(上)
目录 Django基础之模型(models)层 单表查询 必知必会13条 神奇的双下划线查询 多表查询 外键的字段的增删改查 表与表之间的关联查询 基于双下划线的跨表查询(连表查询) 补充知识 Dja ...
- 滚动页面产生动画WOW.js的用法
简介 在一些网页上,当你滚动页面的时候会看到各式各样的元素动画效果,非常动感.WOW.js 就是一款帮助你实现这种 CSS 动画效果的插件.WOW.js 依赖 animate.css,所以它支持 an ...