SpringSecurity自定义权限管理

  • SpringSecurity环境
  • pom.xml
  • SpringSecurity自定义权限管理
  • 自带的权限管理
  • 自定义权限管理


SpringSecurity环境

pom.xml

pom.xml依赖

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
		</dependency>

		<!-- SpringSecurity的依赖 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.70</version>
		</dependency>

SpringSecurity自定义权限管理

自带的权限管理

在configure方法中设置路径对应的角色

.antMatchers("/admin/**").hasRole("ADMIN")

在另一个configure方法中设置用户及其角色

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //新版本必须对密码进行加密,设置加密算法
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                //注册用户:用户名,密码(进行对应的加密),用户的角色,可以对应多个角色
                //以后可以将用户的信息从数据库中读出,这里做演示
                .withUser("admin").password(new BCryptPasswordEncoder().encode("1234")).roles("ADMIN","ANOTHER")
                .and()
                .withUser("user").password(new BCryptPasswordEncoder().encode("1234")).roles();
    }

这个测试之前说过就不说了
现在重要的是如何自定义权限管理,如何从数据库中读取用户的权限,路径对应的权限

自定义权限管理

权限资源获取类 用于获取对应路径的权限

@Component
public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
    	//这边只做了简单的处理
    	//可以从参数object中获取请求的url这里就可以读数据库得到对应的角色权限
    	//这里写死了全部的路径都要ROLE_ADMIN
        List<ConfigAttribute> list = new ArrayList<>();
        list.add(new ConfigAttribute() {
            @Override
            public String getAttribute() {
                return "ROLE_ADMIN";
            }
        });
        return list;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

上面代码可以从参数object中获取请求的url这里就可以读数据库得到对应的角色权限
可以参照下面这个

@Autowired
    private PermissionService permissionService;
    //url对应的权限角色集合
    //key - url
    //value - 有权限的角色集合
    private static HashMap<String,Collection<ConfigAttribute>> map =null;


    private void getResourcePermission(){
        map = new HashMap<>();
        //获取全部的权限
        List<PermissionDO> permissions = permissionService.findAllPermission();
        //遍历全部权限,把url和角色列表加入map
        for(PermissionDO permission : permissions){
            map.put(permission.getUrl(),permission.getRoles());
        }


    }


    //判定url是否在权限中(即url需要那种权限)
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        //如果权限map不存在获取权限
       if (map == null){
           getResourcePermission();
       }
       //获取拦截的url
        HttpServletRequest request = ((FilterInvocation)object).getHttpRequest();
        Iterator<String> iterator = map.keySet().iterator();
        //遍历权限map寻找对应url所需的角色
        while(iterator.hasNext()){
            String url = iterator.next();
            if(new AntPathRequestMatcher(url).matches(request)){
                //url在权限表中返回给AccessDecisionManager的decide方法
                return map.get(url);
            }
        }
        return null;
    }

认证管理器类 这个类是进行授权的类 会对比用户有的权限和路径需要的权限 进行判别

@Component
public class MyAccessDecisionManager implements AccessDecisionManager {
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        //如果请求的资源不需要权限,则直接放行
        if(configAttributes == null || configAttributes.size() <= 0) {
            return;
        }

        //判断用户所拥有的权限是否是资源所需要的权限之一,如果是则放行,否则拦截
        Iterator<ConfigAttribute> iter = configAttributes.iterator();
        while(iter.hasNext()) {
            String needRole = iter.next().getAttribute();
            for(GrantedAuthority grantRole : authentication.getAuthorities()) {
                if(needRole.trim().equals(grantRole.getAuthority().trim())) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("no privilege");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

权限拦截器类 这个类就是要进行配置的 上面两个需要注入进来

@Component
public class MyFilterSecurityInterceptor extends FilterSecurityInterceptor {


    @Autowired
    private FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource;

    @Autowired
    public void setAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
        super.setAccessDecisionManager(myAccessDecisionManager);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        //这里进行拦截的处理
        InterceptorStatusToken token = super.beforeInvocation(fi);
        try {
            /**
             * 执行下一个拦截器
             */
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.afterInvocation(token, null);
        }
    }


    @Override
    public Class<?> getSecureObjectClass() {
        return FilterInvocation.class;
    }

    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.filterInvocationSecurityMetadataSource;
    }


}

进行过滤器配置

http.addFilterAt(filterSecurityInterceptor,FilterSecurityInterceptor.class)
                .exceptionHandling()
                //配置授权失败处理器,权限不足
                .accessDeniedHandler(new AccessDeniedHandler() {
                    @Override
                    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                        response.setContentType("application/json;charset=UTF-8");
                        String message = "无权查看";
                        PrintWriter printWriter = response.getWriter();
                        printWriter.write(message);
                        printWriter.flush();
                        printWriter.close();
                    }
                });

使用postman测试

admin用户

spring auth2 自定义授权界面 springsecurity自定义权限_spring boot


普通用户

spring auth2 自定义授权界面 springsecurity自定义权限_spring_02

一个细节

权限资源获取类中给的角色是 “ROLE_ADMIN”
而配置类configure方法中的角色是"ADMIN"
这是因为自带的添加用户的会增加“ROLE_前缀”而自定义的不会
还有自带的设置路径权限的也不需要加ROLE_

//设置路径和权限
                .antMatchers("/admin/**").hasRole("ADMIN")

如果我们自定义用户的获取就会不一样

@Component
public class MyUserDetailService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Collection<GrantedAuthority> collection = new ArrayList<>();
        collection.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        //如果是admin用户
        if (username.equals("admin"))
            return new User("admin",new BCryptPasswordEncoder().encode("1234"),collection);
        collection.clear();
        //不是admin用户
        collection.add(new SimpleGrantedAuthority("ROLE_NO"));
        return new User("user",new BCryptPasswordEncoder().encode("1234"),collection);
    }
}

上面这个方法中可以改为从数据库中获取角色的所有信息这里我们是自定义的所以要自带ROLE_前缀
配置类需要修改为下面这样

@Qualifier("myUserDetailService")
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }

SpringSecurity到这就结束了。