背景描述

项目中需要做细粒的权限控制,细微至url + httpmethod (满足restful,例如: https://.../xxx/users/1, 某些角色只能查看(HTTP GET), 而无权进行增改删(POST, PUT, DELETE))。
表设计
为避嫌,只列出要用到的关键字段,其余敬请自行脑补。
1.admin_user 管理员用户表, 关键字段( id, role_id )。
2.t_role 角色表, 关键字段( id, privilege_id )。
3.t_privilege 权限表, 关键字段( id, url, method )
三个表的关联关系就不用多说了吧,看字段一眼就能看出。
实现前分析
我们可以逆向思考:
要实现我们的需求,最关键的一步就是让Spring Security的AccessDecisionManager来判断所请求的url + httpmethod 是否符合我们数据库中的配置。然而,AccessDecisionManager并没有来判定类似需求的相关Voter, 因此,我们需要自定义一个Voter的实现(默认注册的AffirmativeBased的策略是只要有Voter投出ACCESS_GRANTED票,则判定为通过,这也正符合我们的需求)。实现voter后,有一个关键参数(Collection
总结一下思路步骤:
1.自定义voter实现。
2.自定义ConfigAttribute实现。
3.自定义SecurityMetadataSource实现。
4.Authentication包含用户实例(这个其实不用说,大家应该都已经这么做了)。
5.自定义GrantedAuthority实现。
项目实战
1.自定义GrantedAuthority实现
UrlGrantedAuthority.java
public class UrlGrantedAuthority implements GrantedAuthority {
private final String httpMethod;
private final String url;
public UrlGrantedAuthority(String httpMethod, String url) {
this.httpMethod = httpMethod;
this.url = url;
}
@Override
public String getAuthority() {
return url;
}
public String getHttpMethod() {
return httpMethod;
}
public String getUrl() {
return url;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UrlGrantedAuthority target = (UrlGrantedAuthority) o;
if (httpMethod.equals(target.getHttpMethod()) && url.equals(target.getUrl())) return true;
return false;
}
@Override
public int hashCode() {
int result = httpMethod != null ? httpMethod.hashCode() : 0;
result = 31 * result + (url != null ? url.hashCode() : 0);
return result;
}
}
2.自定义认证用户实例
public class SystemUser implements UserDetails {
private final Admin admin;
private List<MenuOutput> menuOutputList;
private final List<GrantedAuthority> grantedAuthorities;
public SystemUser(Admin admin, List<AdminPrivilege> grantedPrivileges, List<MenuOutput> menuOutputList) {
this.admin = admin;
this.grantedAuthorities = grantedPrivileges.stream().map(it -> {
String method = it.getMethod() != null ? it.getMethod().getLabel() : null;
return new UrlGrantedAuthority(method, it.getUrl());
}).collect(Collectors.toList());
this.menuOutputList = menuOutputList;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.grantedAuthorities;
}
@Override
public String getPassword() {
return admin.getPassword();
}
@Override
public String getUsername() {
return null;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public Long getId() {
return admin.getId();
}
public Admin getAdmin() {
return admin;
}
public List<MenuOutput> getMenuOutputList() {
return menuOutputList;
}
public String getSalt() {
return admin.getSalt();
}
}
3.自定义UrlConfigAttribute实现
public class UrlConfigAttribute implements ConfigAttribute {
private final HttpServletRequest httpServletRequest;
public UrlConfigAttribute(HttpServletRequest httpServletRequest) {
this.httpServletRequest = httpServletRequest;
}
@Override
public String getAttribute() {
return null;
}
public HttpServletRequest getHttpServletRequest() {
return httpServletRequest;
}
}
4.自定义SecurityMetadataSource实现
public class UrlFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
final HttpServletRequest request = ((FilterInvocation) object).getRequest();
Set<ConfigAttribute> allAttributes = new HashSet<>();
ConfigAttribute configAttribute = new UrlConfigAttribute(request);
allAttributes.add(configAttribute);
return allAttributes;
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
5.自定义voter实现
public class UrlMatchVoter implements AccessDecisionVoter<Object> {
@Override
public boolean supports(ConfigAttribute attribute) {
if (attribute instanceof UrlConfigAttribute) return true;
return false;
}
@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();
for (ConfigAttribute attribute : attributes) {
if (!(attribute instanceof UrlConfigAttribute)) continue;
UrlConfigAttribute urlConfigAttribute = (UrlConfigAttribute) attribute;
for (GrantedAuthority authority : authorities) {
if (!(authority instanceof UrlGrantedAuthority)) continue;
UrlGrantedAuthority urlGrantedAuthority = (UrlGrantedAuthority) authority;
if (StringUtils.isBlank(urlGrantedAuthority.getAuthority())) continue;
//如果数据库的method字段为null,则默认为所有方法都支持
String httpMethod = StringUtils.isNotBlank(urlGrantedAuthority.getHttpMethod()) ? urlGrantedAuthority.getHttpMethod()
: urlConfigAttribute.getHttpServletRequest().getMethod();
//用Spring已经实现的AntPathRequestMatcher进行匹配,这样我们数据库中的url也就支持ant风格的配置了(例如:/xxx/user/**)
AntPathRequestMatcher antPathRequestMatcher = new AntPathRequestMatcher(urlGrantedAuthority.getAuthority(), httpMethod);
if (antPathRequestMatcher.matches(urlConfigAttribute.getHttpServletRequest()))
return ACCESS_GRANTED;
}
}
return ACCESS_ABSTAIN;
}
}
6.自定义FilterSecurityInterceptor实现
public class UrlFilterSecurityInterceptor extends FilterSecurityInterceptor {
public UrlFilterSecurityInterceptor() {
super();
}
@Override
public void init(FilterConfig arg0) throws ServletException {
super.init(arg0);
}
@Override
public void destroy() {
super.destroy();
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
super.doFilter(request, response, chain);
}
@Override
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return super.getSecurityMetadataSource();
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return super.obtainSecurityMetadataSource();
}
@Override
public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource) {
super.setSecurityMetadataSource(newSource);
}
@Override
public Class<?> getSecureObjectClass() {
return super.getSecureObjectClass();
}
@Override
public void invoke(FilterInvocation fi) throws IOException, ServletException {
super.invoke(fi);
}
@Override
public boolean isObserveOncePerRequest() {
return super.isObserveOncePerRequest();
}
@Override
public void setObserveOncePerRequest(boolean observeOncePerRequest) {
super.setObserveOncePerRequest(observeOncePerRequest);
}
}
配置文件关键配置
<security:http>
...
<security:custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR" />
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="daoAuthenticationProvider"/>
</security:authentication-manager>
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<constructor-arg>
<list>
<bean id="authenticatedVoter" class="org.springframework.security.access.vote.AuthenticatedVoter" />
<bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter" />
<bean id="urlMatchVoter" class="com.mobisist.app.security.access.voter.UrlMatchVoter" />
</list>
</constructor-arg>
</bean>
<bean id="securityMetadataSource" class="com.mobisist.app.security.access.UrlFilterInvocationSecurityMetadataSource" />
<bean id="filterSecurityInterceptor"
class="com.mobisist.app.security.access.UrlFilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="securityMetadataSource" ref="securityMetadataSource" />
</bean>
好啦,接下来享受你的Spring Security权限控制之旅吧。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# spring
# security
# 权限
# springsecurity自定义
# 权限控制
# 详解Spring Security 中的四种权限控制方式
# spring security动态配置url权限的2种实现方法
# SpringSecurity动态加载用户角色权限实现登录及鉴权功能
# Spring security实现登陆和权限角色控制
# 解决Spring Security的权限配置不生效问题
# SpringBoot整合Security实现权限控制框架(案例详解)
# Spring security实现权限管理示例
# SpringBoot2.0 整合 SpringSecurity 框架实现用户权限安全管理方法
# Spring Security动态权限的实现方法详解
# 基于Spring Security的动态权限系统设计与实现
# 自定义
# 数据库中
# 就能
# 也就
# 我们可以
# 这也
# 之旅
# 要用
# 这么做
# 多说
# 好啦
# 最关键
# 配置文件
# 大家多多
# 是否符合
# 有一个
# 默认为
# 投出
# 关联关系
# 只要有
相关文章:
教学论文网站制作软件有哪些,写论文用什么软件
?
太平洋网站制作公司,网络用语太平洋是什么意思?
如何在腾讯云免费申请建站?
如何注册花生壳免费域名并搭建个人网站?
免费的流程图制作网站有哪些,2025年教师初级职称申报网上流程?
齐河建站公司:营销型网站建设与SEO优化双核驱动策略
如何在云主机快速搭建网站站点?
临沂网站制作公司有哪些,临沂第四中学官网?
如何选择高效稳定的ISP建站解决方案?
建站之星如何优化SEO以实现高效排名?
定制建站价位费用解析与套餐推荐全攻略
电脑免费海报制作网站推荐,招聘海报哪个网站多?
建站之星安装后如何配置SEO及设计样式?
如何在宝塔面板中创建新站点?
建站之星×万网:智能建站系统+自助建站平台一键生成
独立制作一个网站多少钱,建立网站需要花多少钱?
如何在阿里云域名上完成建站全流程?
小程序网站制作需要准备什么资料,如何制作小程序?
建站之星安全性能如何?防护体系能否抵御黑客入侵?
建站主机SSH密钥生成步骤及常见问题解答?
如何登录建站主机?访问步骤全解析
建站之星北京办公室:智能建站系统与小程序生成方案解析
建站之星价格显示格式升级,你的预算足够吗?
郑州企业网站制作公司,郑州招聘网站有哪些?
,购物网站怎么盈利呢?
建站主机与服务器功能差异如何区分?
如何在Tomcat中配置并部署网站项目?
天河区网站制作公司,广州天河区如何办理身份证?需要什么资料有预约的网站吗?
高端智能建站公司优选:品牌定制与SEO优化一站式服务
网站制作公司排行榜,四大门户网站排名?
电商网站制作公司有哪些,1688网是什么意思?
高端企业智能建站程序:SEO优化与响应式模板定制开发
网站制作与设计教程,如何制作一个企业网站,建设网站的基本步骤有哪些?
如何通过.red域名打造高辨识度品牌网站?
建站之家VIP精选网站模板与SEO优化教程整合指南
网站图片在线制作软件,怎么在图片上做链接?
网站插件制作软件免费下载,网页视频怎么下到本地插件?
实例解析Array和String方法
网站专业制作公司,网站编辑是做什么的?好做吗?工作前景如何?
如何快速配置高效服务器建站软件?
网站制作费用多少钱,一个网站的运营,需要哪些费用?
广州网站设计制作一条龙,广州巨网网络科技有限公司是干什么的?
建站之星下载版如何获取与安装?
建站主机类型有哪些?如何正确选型
,石家庄四十八中学官网?
如何通过IIS搭建网站并配置访问权限?
孙琪峥织梦建站教程如何优化数据库安全?
长沙做网站要多少钱,长沙国安网络怎么样?
洛阳网站制作公司有哪些,洛阳的招聘网站都有哪些?
广东专业制作网站有哪些,广东省能源集团有限公司官网?
*请认真填写需求信息,我们会在24小时内与您取得联系。