技术标签: spring boot security spring security 授权 AccessDecisionManager AccessDecisionVoter
spring security主要分为两部分,认证(authentication)和授权(authority)。
这一篇主要是授权部分,它由FilterSecurityInterceptor逻辑拦截处理,具体通过AccessDecisionManager实现。
系统提供了三种实现方式:
这三种方式都包含了一个AccessDecisionManager(权限控制处理)和多个AccessDecisionVoter(投票项)。
系统默认提供的是基于ROLE(角色)的权限,这里自定义一下,处理 url + httpMethod 方式的权限拦截。
有三种方式可以实现:
方式一:
通过.access 方式实现。
步骤:
1. 自定义MyAuthService:实际权限校验服务
2. WebSecurityConfigurerAdapter配置:注入自定义校验服务
方式二:
通过.accessDecisionManager,覆盖AccessDecisionManager方式实现。
步骤:
1. 自定义AccessDecisionManager: 实现授权逻辑校验。
2. WebSecurityConfigurerAdapter配置:注入自定义AccessDecisionManager
方式三:
通过 添加AccessDecisionVoter投票项处理。
步骤:
1. 自定义AccessDecisionVoter: 实现授权投票逻辑
2. WebSecurityConfigurerAdapter配置:注入自定义AccessDecisionVoter
package demo.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class MyAuthService {
private Logger log = LoggerFactory.getLogger(this.getClass());
/**
*
* @Description: 判断一个请求是否拥有权限。
*
* @auther: csp
* @date: 2019/1/7 下午9:48
* @param request
* @param authentication
* @return: boolean
*
*/
public boolean canAccess(HttpServletRequest request, Authentication authentication) {
Object principal = authentication.getPrincipal();
if(principal == null){
return false;
}
if(authentication instanceof AnonymousAuthenticationToken){
//check if this uri can be access by anonymous
return false;
}
authentication.getAuthorities();
String uri = request.getRequestURI();
//check this uri can be access by this role
// TODO 实际根据权限列表判断。
log.info("=================== myAuth pass ===================");
return true;
}
}
http
.authorizeRequests()
// .access 方式 校验是否有权限。
.antMatchers("/user/**", "/").access("@myAuthService.canAccess(request,authentication)")
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/").and()
.formLogin().loginPage("/login").defaultSuccessUrl("/").failureUrl("/login-error");
package demo.config;
import demo.model.UrlGrantedAuthority;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;
import java.util.Collection;
/**
*
* @Description: 自定义AccessDecisionManager,通过url和httpmethod拦截权限
*
* @auther: csp
* @date: 2019/1/7 下午9:59
*
*/
public class UrlMatchAccessDecisionManager implements AccessDecisionManager {
@Override public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override public boolean supports(Class<?> clazz) {
return true;
}
@Override public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
if (authentication == null) {
throw new AccessDeniedException("无权限!");
}
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
// 请求路径
String url = getUrl(object);
// http 方法
String httpMethod = getMethod(object);
boolean hasPerm = false;
// request请求路径和httpMethod 和权限列表比对。
for (GrantedAuthority authority : authorities) {
if (!(authority instanceof UrlGrantedAuthority))
continue;
UrlGrantedAuthority urlGrantedAuthority = (UrlGrantedAuthority) authority;
if (StringUtils.isEmpty(urlGrantedAuthority.getAuthority()))
continue;
//如果method为null,则默认为所有类型都支持
String httpMethod2 = (!StringUtils.isEmpty(urlGrantedAuthority.getHttpMethod())) ?
urlGrantedAuthority.getHttpMethod() :
httpMethod;
//AntPathRequestMatcher进行匹配,url支持ant风格(如:/user/**)
AntPathRequestMatcher antPathRequestMatcher = new AntPathRequestMatcher(urlGrantedAuthority.getAuthority(),
httpMethod2);
if (antPathRequestMatcher.matches(((FilterInvocation) object).getRequest())) {
hasPerm = true;
break;
}
}
if (!hasPerm) {
throw new AccessDeniedException("无权限!");
}
}
/**
* 获取请求中的url
*/
private String getUrl(Object o) {
//获取当前访问url
String url = ((FilterInvocation) o).getRequestUrl();
int firstQuestionMarkIndex = url.indexOf("?");
if (firstQuestionMarkIndex != -1) {
return url.substring(0, firstQuestionMarkIndex);
}
return url;
}
private String getMethod(Object o) {
return ((FilterInvocation) o).getRequest().getMethod();
}
}
http
.authorizeRequests()
// 覆盖默认的AffirmativeBased授权逻辑。
.accessDecisionManager(getAccessDecisionManager())
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/").and()
.formLogin().loginPage("/login").defaultSuccessUrl("/").failureUrl("/login-error");
package demo.service;
import demo.model.UrlGrantedAuthority;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;
import java.util.Collection;
/**
*
* @Description: 增加一个授权逻辑投票项,根据url和httpmethod判断权限。
*
* @auther: csp
* @date: 2019/1/7 下午10:03
*
*/
public class UrlMatchVoter implements AccessDecisionVoter<Object> {
@Override public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override public boolean supports(Class<?> clazz) {
return true;
}
@Override public int vote(Authentication authentication, Object object,
Collection<ConfigAttribute> attributes) {
if (authentication == null) {
return ACCESS_DENIED;
}
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
// 请求路径
String url = getUrl(object);
// http 方法
String httpMethod = getMethod(object);
boolean hasPerm = false;
for (GrantedAuthority authority : authorities) {
if (!(authority instanceof UrlGrantedAuthority))
continue;
UrlGrantedAuthority urlGrantedAuthority = (UrlGrantedAuthority) authority;
if (StringUtils.isEmpty(urlGrantedAuthority.getAuthority()))
continue;
//如果method为null,则默认为所有类型都支持
String httpMethod2 = (!StringUtils.isEmpty(urlGrantedAuthority.getHttpMethod())) ?
urlGrantedAuthority.getHttpMethod() :
httpMethod;
//AntPathRequestMatcher进行匹配,url支持ant风格(如:/user/**)
AntPathRequestMatcher antPathRequestMatcher = new AntPathRequestMatcher(urlGrantedAuthority.getAuthority(),
httpMethod2);
if (antPathRequestMatcher.matches(((FilterInvocation) object).getRequest())) {
hasPerm = true;
break;
}
}
if (!hasPerm) {
return ACCESS_DENIED;
}
return ACCESS_GRANTED;
}
/**
* 获取请求中的url
*/
private String getUrl(Object o) {
//获取当前访问url
String url = ((FilterInvocation) o).getRequestUrl();
int firstQuestionMarkIndex = url.indexOf("?");
if (firstQuestionMarkIndex != -1) {
return url.substring(0, firstQuestionMarkIndex);
}
return url;
}
private String getMethod(Object o) {
return ((FilterInvocation) o).getRequest().getMethod();
}
}
http
// 将tokenfilter追加进去,筛选出来tokenLogin逻辑。
.addFilterBefore(getTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.logout().logoutUrl("/logout").logoutSuccessUrl("/").and()
.formLogin().loginPage("/login").defaultSuccessUrl("/").failureUrl("/login-error").permitAll().and()
.authorizeRequests()
.antMatchers(MyTokenAuthenticationFilter.SPRING_SECURITY_RESTFUL_LOGIN_URL).permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasRole("USER")
.anyRequest().authenticated()
// 修改授权相关逻辑
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
public <O extends FilterSecurityInterceptor> O postProcess(
O fsi) {
// // 覆盖SecurityMetadataSource
// fsi.setSecurityMetadataSource(fsi.getSecurityMetadataSource());
// // 覆盖AccessDecisionManager
// fsi.setAccessDecisionManager(getAccessDecisionManager());
// 为默认的AffirmativeBased逻辑增加投票项,
AccessDecisionManager accessDecisionManaer = fsi.getAccessDecisionManager();
if (accessDecisionManager instanceof AbstractAccessDecisionManager) {
((AbstractAccessDecisionManager) accessDecisionManager).getDecisionVoters().add(new UrlMatchVoter());
}
return fsi;
}
});
user 通过角色授权(ROLE),
admin 通过自定义投票项 UrlMatchVoter 授权。
由于AuthenticationManager使用的是默认的AffirmativeBased,所以只要有一个通过,则说明有权限。
最近需要新做一个项目,后端是spring boot ,由于涉及到用户权限验证,于是去看了看spring security,在这里记录一下配置过程 首先引入依赖 我这里应该是比较新的版本,貌似我查了下是5以上的,顺便贴出spring boot版本 这时候启动项目,访问任意路径,你会发现页面转到/login页面 这是security内置的一个页面,如果不做任何配置时,未登录的请求将会被拦截到...
pom依赖 加入依赖后启动项目就已经对资源实现保护,用户名默认user,密码在项目启动中默认打印在控制台中. 当然也可以在application.yml中配置 以上作为学习简单了解下,实际这么配怕是要被祭天… 下面将从数据库中提取用户来实现 第一步:WebSecurityConfigurerAdapter 第二步:UserDetailsService 第三步:UserDetails ...
1、基础概念 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring的IOC,DI,AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为安全控制编写大量重复代码的工作。 2、核心API解读 1、SecurityContextHolde...
随笔记录 demo目前在Spring Boot 中整合了Security,Mybatis,SQLite等组件 1.Maven中的POM文件 2.入口类增加Mybatis相关扫描注解 3.appliaaction.yml文件整体配置 4.创建authentication包,进行security的相关配置类 5.自定义管理类 到这里Security的相关配置阶段完成。...
.pom文件需要引入 配置中的URL不要入库,csrf token 默认是true spring boot 的spring security配置代替spring mvc的spring security xxx.xml配置 拦截器实现,根据需求实现。若之前的数据表和spring security不兼容,可以更具数据表的情况,对所有requestUrl放行,拦截特定的url。这里去掉实现逻辑。 加载已...
spring boot security(一) 配置认证和授权 通过继承WebSecurityConfigurerAdapter,可以重写其认证和授权的逻辑。 实现UserDetailsService接口 注入bean BCryptPasswordEncoder Encoded password does not look like BCrypt 数据库传递的密码没有经过BCrypt加密。 解决方...
Spring boot整合Security 概述 Spring Security 是spring项目之中的一个安全模块 WebSecurityConfigurerAdapter: 自定义Security策略 AuthenticationManagerBuilder: 自定义认证策略 @EnableWebSecurity: 开启WebSecurity模式 Spring Security的两个主要目标...
spring security自定义认证和自定义授权 第一种方式:自定义login接口: 通过配置对自定义的登录放行,实现登录方式; 认证是否登录的对象是继承BasicAuthenticationFilter类,(认证方式我使用token认证,在登录接口里面把信息缓存到redis),通过BasicAuthenticationFilter的子类去做认证,代码就不贴了,大概的逻辑是这样的:判断指定的请...
接着上一篇博客继续说明Spring Security 与Spring Boot结合后的使用 框架原理流程: 1 容器启动,spring security 从库里头加载权限信息 并以map的形式存取 (HashMap resourceMap key:资源url,value:角色名称 ROLE_为前缀的值) 2 用户进行登录操作: 加载用户的角色列表(Collection ) loadUserByUs...
Pom 依赖: 从源码中(源码教程),或者Spring Security 官方文档我们可以知道Spring Security将账号、密码等信息是封装到UsernamePasswordAuthenticationToken 对象中去,那么我们可以继承这个类来自定义我们自己的东西。 从源码中我们可以知道,AuthenticationManager 是一个总览全局的对象,我们登陆的时候就是将封装好的Jw...