虚位以待(AD)
虚位以待(AD)
首页 > 软件编程 > Java编程 > java中自定义Spring Security权限控制管理示例(实战篇)

java中自定义Spring Security权限控制管理示例(实战篇)
类别:Java编程   作者:码皇   来源:互联网   点击:

本篇文章主要介绍了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<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自定