当前位置:   article > 正文

角色权限实现方案_基于角色的权限管理系统一个用户同时拥有不同的角色时,他拥有这些角色的所有权限

基于角色的权限管理系统一个用户同时拥有不同的角色时,他拥有这些角色的所有权限

角色权限实现方案

1、背景

本系统要求不同用户登陆后,可以操作不同的系统功能。所以要求每个登录用户具有不同的角色,每个角色具有不同的权限;同时要求后期添加角色、权限时,不需要修改系统实现逻辑。

2、角色权限方案

角色权限实现框架有Spring Security与Shiro两种,因Jhispter安全认证采用的Spring Security框架,因此选择Spring Security。

Spring Securit权限认证方式有:

  • 表达式控制 URL 路径权限;
  • 表达式控制方法权限;
  • 使用过滤注解;
  • 动态权限;

、表达式控制 URL 路径权限:通过表达式控制 URL 路径权限。 

Spring Security 支持在 URL 和方法权限控制时使用 SpEL 表达式,如果表达式返回值为 true 则表示需要对应的权限,否则表示不需要对应的权限。提供表达式的类是 SecurityExpressionRoot,该类对应的表达式有:

表达式

备注

hasRole

用户具备某个角色即可访问资源

hasAnyRole

用户具备多个角色中的任意一个即可访问资源

hasAuthority

类似于 hasRole

hasAnyAuthority

类似于 hasAnyRole

permitAll

统统允许访问

denyAll

统统拒绝访问

isAnonymous

判断是否匿名用户

isAuthenticated

判断是否认证成功

isRememberMe

判断是否通过记住我登录的

isFullyAuthenticated

判断是否用户名/密码登录的

principle

当前用户

authentication

从 SecurityContext 中提取出来的用户对象

 

 

使用配置:

protected void configure(HttpSecurity http) throws Exception {

    http.authorizeRequests()

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

           .antMatchers("/user/**").hasAnyRole("admin","user")

           .anyRequest().authenticated()

           .and()

           ...

}

二、表达式控制方法权限:通过在方法上添加注解来控制权限。

在方法上添加注解控制权限,需要我们首先开启注解的使用,在 Spring Security 配置类上添加如下内容:

@Configuration

@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    ...

    ...

}

这个配置开启了三个注解,分别是:

  1. @PreAuthorize:方法执行前进行权限检查;
  2. @PostAuthorize:方法执行后进行权限检查;
  3. @Secured:类似于 @PreAuthorize;

@Service

public class HelloService {

    @PreAuthorize("principal.username.equals('javaboy')")

    public String hello() {

        return "hello";

    }

 

    @PreAuthorize("hasRole('admin')")

    public String admin() {

        return "admin";

    }

 

    @Secured({"ROLE_user"})

    public String user() {

        return "user";

    }

 

    @PreAuthorize("#age>98")

    public String getAge(Integer age) {

        return String.valueOf(age);

    }

、使用过滤注解

Spring Security 中还有两个过滤函数 @PreFilter 和 @PostFilter,可以根据给出的条件,自动移除集合中的元素。

@PostFilter("filterObject.lastIndexOf('2')!=-1")

public List<String> getAllUser() {

    List<String> users = new ArrayList<>();

    for (int i = 0; i < 10; i++) {

        users.add("javaboy:" + i);

    }

    return users;

}

@PreFilter(filterTarget = "ages",value = "filterObject%2==0")

public void getAllAge(List<Integer> ages,List<String> users) {

    System.out.println("ages = " + ages);

    System.out.println("users = " + users);

}

四、动态权限:动态权限主要通过重写拦截器和决策器来实现。

 

主要使用的组件:

AuthenticationProcessingFilter

AbstractSecurityInterceptor

AuthenticationManager

AccessDecisionManager等组件来支撑。

 

整个流程,用户登陆,会被AuthenticationProcessingFilter拦截,调用AuthenticationManager的实现,而且AuthenticationManager会调用ProviderManager来获取用户验证信息(不同的Provider调用的服务不同,因为这些信息可以是在数据库上,可以是在LDAP服务器上,可以是xml配置文件上等),如果验证通过后会将用户的权限信息封装一个User放到spring的全局缓存SecurityContextHolder中,以备后面访问资源时使用。

访问资源(即授权管理),访问url时,会通过AbstractSecurityInterceptor拦截器拦截,其中会调用FilterInvocationSecurityMetadataSource的方法来获取被拦截url所需的全部权限,在调用授权管理器AccessDecisionManager,这个授权管理器会通过spring的全局缓存SecurityContextHolder获取用户的权限信息,还会获取被拦截的url和被拦截url所需的全部权限,然后根据所配的策略(有:一票决定,一票否定,少数服从多数等),如果权限足够,则返回,权限不够则报错并调用权限不足页面。

 

 

3、实施方案选择

根据四个方案的不同以及业务需求,动态权限更加符合系统需求,因此采用动态权限来实现本系统的角色权限控制功能。

 

4、基于Restful风格权限实现及设计

数据库设计

权限表的业务代码

PermissionDao.java

public interface PermissionDao {

    public List<Permission> findAll();

    public List<Permission> findByAdminUserId(int userId);

}

UserDao.java

public interface UserDao {

    public User findByUserName(String username);

}

SpringSecurity 配置修改

  1. 修改 WebSecurityConfig.java

@Configuration

@EnableWebSecurity

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired

    private MyFilterSecurityInterceptor myFilterSecurityInterceptor;

    @Bean

    UserDetailsService customUserService(){ //注册UserDetailsService 的bean

        return new CustomUserService();

    }

 

    @Override

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(customUserService()); //user Details Service验证

 

    }

 

    @Override

    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()

                .anyRequest().authenticated() //任何请求,登录后可以访问

                .and()

                .formLogin()

                .loginPage("/login")

                .failureUrl("/login?error")

                .permitAll() //登录页面用户任意访问

                .and()

                .logout().permitAll(); //注销行为任意访问

        http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);

    }

}

2、实现 GrantedAuthority 接口

public class MyGrantedAuthority implements GrantedAuthority {

    private String url;

private String method;

 

3、修改CustomUserService

@Service

public class CustomUserService implements UserDetailsService { //自定义UserDetailsService 接口

 

    @Autowired

    UserDao userDao;

    @Autowired

    PermissionDao permissionDao;

 

    public UserDetails loadUserByUsername(String username) {

        User user = userDao.findByUserName(username);

        if (user != null) {

            List<Permission> permissions = permissionDao.findByAdminUserId(user.getId());

            List<GrantedAuthority> grantedAuthorities = new ArrayList <>();

            for (Permission permission : permissions) {

                if (permission != null && permission.getName()!=null) {

 

GrantedAuthority grantedAuthority = new MyGrantedAuthority(permission.getUrl(), permission.getMethod());

                    grantedAuthorities.add(grantedAuthority);

 

                //1:此处将权限信息添加到 GrantedAuthority 对象中,在后面进行全权限验证时会使用GrantedAuthority 对象。

                grantedAuthorities.add(grantedAuthority);

                }

            }

            return new User(user.getUsername(), user.getPassword(), grantedAuthorities);

        } else {

            throw new UsernameNotFoundException("admin: " + username + " do not exist!");

        }

    }

 

}

4、修改 MyAccessDecisionManager 的decide 方法

@Service

public class MyAccessDecisionManager implements AccessDecisionManager {

 

    //decide 方法是判定是否拥有权限的决策方法

    @Override

    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {

 

        HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();

        String url, method;

        AntPathRequestMatcher matcher;

        for (GrantedAuthority ga : authentication.getAuthorities()) {

            if (ga instanceof MyGrantedAuthority) {

                MyGrantedAuthority urlGrantedAuthority = (MyGrantedAuthority) ga;

                url = urlGrantedAuthority.getPermissionUrl();

                method = urlGrantedAuthority.getMethod();

                matcher = new AntPathRequestMatcher(url);

                if (matcher.matches(request)) {

                    //当权限表权限的method为ALL时表示拥有此路径的所有请求方式权利。

                    if (method.equals(request.getMethod()) || "ALL".equals(method)) {

                        return;

                    }

                }

            } else if (ga.getAuthority().equals("ROLE_ANONYMOUS")) {//未登录只允许访问 login 页面

                matcher = new AntPathRequestMatcher("/login");

                if (matcher.matches(request)) {

                    return;

                }

                }

        throw new AccessDeniedException("no right");

    }

 

    @Override

    public boolean supports(ConfigAttribute attribute) {

        return true;

    }

 

    @Override

    public boolean supports(Class<?> clazz) {

        return true;

    }

}

5、修改MyInvocationSecurityMetadataSourceService 的getAttributes 方法

 

@Service

public class MyInvocationSecurityMetadataSourceService  implements

        FilterInvocationSecurityMetadataSource {

 

    //此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。

    //因为我不想每一次来了请求,都先要匹配一下权限表中的信息是不是包含此url,

    // 我准备直接拦截,不管请求的url 是什么都直接拦截,然后在MyAccessDecisionManager的decide 方法中做拦截还是放行的决策。

    //所以此方法的返回值不能返回 null 此处我就随便返回一下。

    @Override

    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {

        Collection<ConfigAttribute> co=new ArrayList<>();

        co.add(new SecurityConfig("null"));

        return co;

    }

 

    @Override

    public Collection<ConfigAttribute> getAllConfigAttributes() {

        return null;

    }

 

    @Override

    public boolean supports(Class<?> clazz) {

        return true;

    }

}

4、角色、权限增删改查功能(省略)

 

 

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号