下面就是关于“Spring Security权限管理实现接口动态权限控制”的完整攻略:
在Spring Security中,我们可以使用基于注解的安全性,以控制方法响应、请求类型等。但是,如果我们需要跟具体的业务数据绑定的话,我们就需要根据规则来控制具体的访问权限。
在这种情况下,就需要使用Spring Security提供的“动态授权”功能了。本文将介绍如何使用动态授权功能来精细管理用户权限。
Spring Security中的动态授权,是通过实现权限控制的接口来达到的。主要通过实现AccessDecisionManager接口和AccessDecisionVoter接口来实现。
AccessDecisionManager接口提供了访问决策,它用于限定某个Subject允许访问哪种资源。
AccessDecisionVoter接口是AccessDecisionManager的一个具体实现。它可以进行“投票”,来判断是否允许访问某个资源。
下面是具体的实现步骤:
首先,在项目中定义一个自定义的权限管理器,在这里我们继承了AccessDecisionManager接口。
定义完成后,重写父接口中的方法decide()
。decide()
方法会对用户的拥有角色和尝试访问资源进行比较,如果判断用户拥有该资源访问权限则返回,否则就抛出InvalidAuthorityException异常。
@Service
public class CustomAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
if(configAttributes == null) {
return;
}
Iterator<ConfigAttribute> iterator = configAttributes.iterator();
while(iterator.hasNext()) {
ConfigAttribute configAttribute = iterator.next();
String needRole = configAttribute.getAttribute();
for(GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
if(grantedAuthority.getAuthority().equals(needRole)) {
return;
}
}
}
throw new AccessDeniedException("您没有访问该资源的权限");
}
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
定义完权限管理器后,我们就要定义自定义的投票器了,继承AccessDecisionVoter接口,这个类主要负责解析用户请求中的资源路径、资源类型和权限集合,进行投票决策。
和之前一样,我们要实现该接口中重写的方法vote()
。根据用户的访问路径和权限,它会“投票”给权限管理器,最终决定用户是否有权访问。
在这里,我们实现了两个投票器来进行权限控制。其中,一些接口需要admin角色才能访问,而其他接口不需要。
示例代码:
@Component
public class UrlAccessDecisionVoter 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) {
int result = AccessDecisionVoter.ACCESS_ABSTAIN;
Object principal = authentication.getPrincipal();
if (!(principal instanceof UserDetails)) {
return result;
}
UserDetails user = (UserDetails) principal;
for (ConfigAttribute attribute : attributes) {
if ("ROLE_ADMIN".equals(attribute.getAttribute())) {
result = AccessDecisionVoter.ACCESS_DENIED;
for (GrantedAuthority authority : user.getAuthorities()) {
if ("ROLE_ADMIN".equals(authority.getAuthority())) {
return AccessDecisionVoter.ACCESS_GRANTED;
}
}
} else {
result = AccessDecisionVoter.ACCESS_GRANTED;
}
}
return result;
}
}
在web.xml中配置remember-me和session的过期时间。
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>RememberMeFilter</filter-name>
<filter-class>org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>RememberMeFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
最后,在Spring Security的配置文件SecurityConfig.java中,我们要做两件事情:
示例代码:
@Configuration
@EnableWebSecurity
@ComponentScan(basePackages = {"com.example.demo"})
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomAccessDecisionManager customAccessDecisionManager;
@Autowired
private UrlAccessDecisionVoter urlAccessDecisionVoter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasRole("USER")
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().logoutUrl("/logout").permitAll()
.and()
.exceptionHandling().accessDeniedPage("/noAuth");
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.ignoring().antMatchers("/css/**", "/js/**");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("123456").roles("ADMIN", "USER")
.and()
.withUser("user").password("123456").roles("USER");
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.ignoring().antMatchers("/css/**", "/js/**");
}
@Bean
public FilterRegistrationBean<Filter> registrationBean() {
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new DelegatingFilterProxy());
registrationBean.addUrlPatterns("/*");
registrationBean.setName("springSecurityFilterChain");
return registrationBean;
}
@Bean
public AccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter<?>> decisionVoters = new ArrayList<>();
decisionVoters.add(urlAccessDecisionVoter);
//这里可以按照自己需求添加更多的投票器
AffirmativeBased accessDecisionManager = new AffirmativeBased(decisionVoters);
accessDecisionManager.setDecisionVoters(decisionVoters);
return customAccessDecisionManager;
}
}
下面是两个基于Spring Security动态授权的示例:
在上面的代码中,我们在SecurityConfig.java中配置了urlAccessDecisionVoter
,用于具体控制某些URL只允许admin角色访问。
我们可以使用这个功能来控制某些URL,比如在员工管理系统中,启用了一个特殊的功能模块,只允许部门经理和人事经理访问员工数据。
代码:
@GetMapping("/employee/list")
@Log(title = "员工管理", businessType = BusinessType.LIST)
public AjaxResult<List<Employee>> getEmployeeList() {
return ajaxResponse(employeeService.getAllEmployees());
}
@PostMapping("/employee/add")
@Log(title = "员工管理", businessType = BusinessType.INSERT)
public AjaxResult<String> addEmployee(@RequestBody Employee employee) {
if(employeeService.addEmployee(employee) == 0) {
return ajaxFailed("添加员工失败,请检查员工信息后重试");
}
return ajaxResponse("添加员工成功!");
}
如果一个URL的访问权限比较低,比如说是一个报道页面,或者是一个新闻页面,我们可以让它不受权限管理器的控制,直接访问。
以上就是Spring Security权限管理实现接口动态权限控制的完整攻略,通过实现AccessDecisionManager接口和AccessDecisionVoter接口,可以实现对接口的权限控制。动态授权功能可以根据不同的需求对用户进行权限管理,灵活性很高。
本文链接:http://task.lmcjl.com/news/714.html