java中自定义Spring Security权限控制管理示例(实战篇)

网友投稿 250 2023-06-14


java中自定义Spring Security权限控制管理示例(实战篇)

背景描述

项目中需要做细粒的权限控制,细微至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 menuOutputList;

private final List grantedAuthorities;

public SystemUser(Admin admin, List grantedPrivileges, List 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 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 getAttributes(Object object) throws IllegalArgumentException {

final HttpServletRequest request = ((FilterInvocation) object).getRequest();

Set allAttributes = new HashSet<>();

ConfigAttribute configAttribute = new UrlConfigAttribute(request);

allAttributes.add(configAttribute);

return allAttributes;

}

@Override

public Collection getAllConfigAttributes() {

return null;

}

@Override

public boolean supports(Class> clazz) {

return FilterInvocation.class.isAssignableFrom(clazz);

}

}

5.自定义voter实现

public class UrlMatchVoter implements AccessDecisionVoter {

@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 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.getHttpServletRequesHFgDbqt().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);

}

}

配置文件关键配置

...

class="com.mobisist.app.security.access.UrlFilterSecurityInterceptor">

class="com.mobisist.app.security.access.UrlFilterSecurityInterceptor">

好啦,接下来享受你的Spring Security权限控制之旅吧。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring 依赖注入的几种方式详解
下一篇:Easyui Tree获取当前选择节点的所有顶级父节点
相关文章

 发表评论

暂时没有评论,来抢沙发吧~