SpringSecurity(三)自定义权限管理

  • Post author:
  • Post category:其他




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用户

在这里插入图片描述

普通用户

在这里插入图片描述

一个细节

权限资源获取类中给的角色是 “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到这就结束了。



版权声明:本文为weixin_44046865原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。