html5中文学习网

您的位置: 首页 > 网络编程 > java教程 » 正文

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

[ ] 已经帮助:人解决问题

背景描述tNQHTML5中文学习网 - HTML5先行者学习网

项目中需要做细粒的权限控制,细微至url + httpmethod (满足restful,例如: https://.../xxx/users/1, 某些角色只能查看(HTTP GET), 而无权进行增改删(POST, PUT, DELETE))。tNQHTML5中文学习网 - HTML5先行者学习网

表设计tNQHTML5中文学习网 - HTML5先行者学习网

为避嫌,只列出要用到的关键字段,其余敬请自行脑补。tNQHTML5中文学习网 - HTML5先行者学习网
tNQHTML5中文学习网 - HTML5先行者学习网

1.admin_user 管理员用户表, 关键字段( id, role_id )。tNQHTML5中文学习网 - HTML5先行者学习网
tNQHTML5中文学习网 - HTML5先行者学习网

2.t_role 角色表, 关键字段( id, privilege_id )。tNQHTML5中文学习网 - HTML5先行者学习网
tNQHTML5中文学习网 - HTML5先行者学习网

3.t_privilege 权限表, 关键字段( id, url, method )tNQHTML5中文学习网 - HTML5先行者学习网

三个表的关联关系就不用多说了吧,看字段一眼就能看出。tNQHTML5中文学习网 - HTML5先行者学习网

实现前分析tNQHTML5中文学习网 - HTML5先行者学习网

我们可以逆向思考:tNQHTML5中文学习网 - HTML5先行者学习网

要实现我们的需求,最关键的一步就是让Spring Security的AccessDecisionManager来判断所请求的url + httpmethod 是否符合我们数据库中的配置。然而,AccessDecisionManager并没有来判定类似需求的相关Voter, 因此,我们需要自定义一个Voter的实现(默认注册的AffirmativeBased的策略是只要有Voter投出ACCESS_GRANTED票,则判定为通过,这也正符合我们的需求)。实现voter后,有一个关键参数(CollectiontNQHTML5中文学习网 - HTML5先行者学习网

总结一下思路步骤:tNQHTML5中文学习网 - HTML5先行者学习网

1.自定义voter实现。tNQHTML5中文学习网 - HTML5先行者学习网

2.自定义ConfigAttribute实现。tNQHTML5中文学习网 - HTML5先行者学习网

3.自定义SecurityMetadataSource实现。tNQHTML5中文学习网 - HTML5先行者学习网

4.Authentication包含用户实例(这个其实不用说,大家应该都已经这么做了)。tNQHTML5中文学习网 - HTML5先行者学习网

5.自定义GrantedAuthority实现。tNQHTML5中文学习网 - HTML5先行者学习网

项目实战tNQHTML5中文学习网 - HTML5先行者学习网

1.自定义GrantedAuthority实现tNQHTML5中文学习网 - HTML5先行者学习网

UrlGrantedAuthority.javatNQHTML5中文学习网 - HTML5先行者学习网
tNQHTML5中文学习网 - HTML5先行者学习网

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.自定义认证用户实例tNQHTML5中文学习网 - HTML5先行者学习网
tNQHTML5中文学习网 - HTML5先行者学习网

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实现tNQHTML5中文学习网 - HTML5先行者学习网
tNQHTML5中文学习网 - HTML5先行者学习网

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实现tNQHTML5中文学习网 - HTML5先行者学习网
tNQHTML5中文学习网 - HTML5先行者学习网

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实现tNQHTML5中文学习网 - HTML5先行者学习网
tNQHTML5中文学习网 - HTML5先行者学习网

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实现tNQHTML5中文学习网 - HTML5先行者学习网
tNQHTML5中文学习网 - HTML5先行者学习网

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);  }}

配置文件关键配置tNQHTML5中文学习网 - HTML5先行者学习网
tNQHTML5中文学习网 - HTML5先行者学习网

<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权限控制之旅吧。tNQHTML5中文学习网 - HTML5先行者学习网

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。tNQHTML5中文学习网 - HTML5先行者学习网

(责任编辑:)
推荐书籍
推荐资讯
关于HTML5先行者 - 联系我们 - 广告服务 - 友情链接 - 网站地图 - 版权声明 - 人才招聘 - 帮助