@Configuration
public class WebSerurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserServiceImpl userService;
@Autowired
private MyFilter filter;
@Autowired
private RoleAccessDecisionManager roleAccessDecisionManager;
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/student/**").hasRole("student")
.antMatchers("/teacher/**").hasRole("teacher")
.antMatchers("/admin/**").hasRole("admin")
.anyRequest().authenticated()
.and().formLogin().permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
...
}
}
从代码中,我们可以看到,每次我们添加一个角色,就需要添加角色所对应的权限路径。当我们有越来越多的角色时,我们需要不断往我们的代码中进行添加,这样做显然是很不好的。所以,接下来,我们要把角色和路径都写在数据库中,当我们添加一个角色或者修改角色对应的权限路径时,只需要往数据库进行修改即可。
1.获取当前路径所对应的全部权限(角色)
将问题进行细分:
获取当前请求路径url
获取数据库全部有权限(角色)的url
将当前请求url跟数据库中的url进行对比
(1)如果在数据库中,就要获取当前请求对应的权限(角色)
(2)如果不在,则不用返回权限信息。
getAttributes()方法:返回本次访问需要的权限,可以有多个权限。
@Component
public class MyFilter implements FilterInvocationSecurityMetadataSource {
@Autowired
private UrlMapper urlMapper;
AntPathMatcher antPathMatcher = new AntPathMatcher();
/ //获取当前url对应的权限
@Override
public Collection getAttributes(Object o) throws IllegalArgumentException {
//1.获取当前url
String requestUrl = ((FilterInvocation) o).getRequestUrl();
//2.获取数据库
List allPath = pathMapper.getAllPath();
//3.判断当前url和allPath是否匹配
for (Path path : allPath) {
if(new AntPathMatcher().match(path.getPattern(),requestUrl)){
//4.如果匹配上了,获取这个路径的权限
List rolesList = roleMapper.getRolesByPid(path.getId());
//5.返回出去
String [] rs = new String[rolesList.size()];
for (int i = 0; i < rolesList.size(); i++) {
rs[i] = rolesList.get(i).getRname();
}
return SecurityConfig.createList(rs);
}
}
return null;
}
@Override
public Collection getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class aClass) {
return true;
}
}
2.权限决策(获取当前用户所对应的权限)
有了权限资源,知道了当前访问的url所需要的具体权限,接下来就是决策当前的访问是否能够通过权限验证了。
AccessDecisionManager:决策管理器
实现AccessDecisionManager接口,自定义决策管理器。
decide()方法有三个参数:
authentication: 当前用户对应的信息
object:包含客户端发起的请求的requset信息
collection 当前路径对应的权限
判断该用户对应的权限信息是否跟当前路径对应的权限信息相等。
@Component
public class RoleAccessDecisionManager implements AccessDecisionManager {
/**
* decide 方法是判定是否拥有权限的决策方法,
* @param authentication 当前用户的信息
* @param object 包含客户端发起的请求的requset信息
* @param collection 当前路径对应的权限
* @throws AccessDeniedException
* @throws InsufficientAuthenticationException
*/
@Override
public void decide(Authentication authentication, Object object, Collection collection) throws AccessDeniedException, InsufficientAuthenticationException {
Collection authorities = authentication.getAuthorities();
for (GrantedAuthority authority : authorities) {
for (ConfigAttribute c : collection) {
if(c.getAttribute().equals(authority.getAuthority())){
return;
}
}
}
throw new AccessDeniedException("当前访问没有权限");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class aClass) {
return true;
}
}
3.在WebSerurityConfig.java进行配置
@Configuration
public class WebSerurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserServiceImpl userService;
@Autowired
private MyFilter filter;
@Autowired
private RoleAccessDecisionManager roleAccessDecisionManager;
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor() {
@Override
public O postProcess(O o) {
o.setAccessDecisionManager(roleAccessDecisionManager);
o.setSecurityMetadataSource(filter);
return o;
}
})
// .antMatchers("/student/**").hasRole("student")
// .antMatchers("/teacher/**").hasRole("teacher")
// .antMatchers("/admin/**").hasRole("admin")
.anyRequest().authenticated()
.and().formLogin().permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.inMemoryAuthentication().
// withUser("msj").password(passwordEncoder().encode("msj")).roles("student");
auth.userDetailsService(userService);
}
}