千家信息网

如何解决Spring Security的权限配置不生效问题

发表于:2025-01-17 作者:千家信息网编辑
千家信息网最后更新 2025年01月17日,这篇文章主要介绍如何解决Spring Security的权限配置不生效问题,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Spring Security权限配置不生效在集成Spr
千家信息网最后更新 2025年01月17日如何解决Spring Security的权限配置不生效问题

这篇文章主要介绍如何解决Spring Security的权限配置不生效问题,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

Spring Security权限配置不生效

在集成Spring Security做接口权限配置时,在给用户配置的权限后,还是一直显示"无权限"或者"权限不足"。

1、不生效的例子

接口

@RequestMapping("/admin")    @ResponseBody    @PreAuthorize("hasRole('ADMIN')")    public String printAdmin() {        return "如果你看见这句话,说明你有ROLE_ADMIN角色";    }    @RequestMapping("/user")    @ResponseBody    @PreAuthorize("hasRole('USER')")    public String printUser() {        return "如果你看见这句话,说明你有ROLE_USER角色";    }

SecurityConfig

  .and()      .authorizeRequests()      .antMatchers("/user").hasAnyRole("USER")       .antMatchers("/admin").hasAnyRole("ADMIN")      .anyRequest().authenticated() //必须授权才能范围

用户携带权限

2、解决办法

经测试,只有用户携带权限的字段为 "ROLE_" + 接口/配置 中的权限字段,才能控制生效,举例:

将上面的用户携带权限改为

Spring Security动态配置权限

导入依赖

    org.springframework.boot    spring-boot-starter-security    org.springframework.boot    spring-boot-starter-web    org.mybatis.spring.boot    mybatis-spring-boot-starter    2.1.3    com.alibaba    druid-spring-boot-starter    1.1.22    mysql    mysql-connector-java    runtime    5.1.46    org.springframework.boot    spring-boot-starter-test    test                        org.junit.vintage            junit-vintage-engine                org.springframework.security    spring-security-test    test

相关配置

application.properties

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/javaboy?useUnicode=true&characterEncoding=utf8&serverTimezone=UTCspring.datasource.username=rootspring.datasource.password=rootspring.datasource.type=com.alibaba.druid.pool.DruidDataSource

实体类User,Role,Menu

这里要实现UserDetails接口,这个接口好比一个规范。防止开发者定义的密码变量名各不相同,从而导致springSecurity不知道哪个方法是你的密码

public class User implements UserDetails {    private Integer id;    private String username;    private String password;    private Boolean enabled;    private Boolean locked;    private List roleList;    @Override    public Collection getAuthorities() {        List authorities = new ArrayList<>();        for (Role role : roleList) {            authorities.add(new SimpleGrantedAuthority(role.getName()));        }        return authorities;    }    @Override    public String getPassword() {        return password;    }    @Override    public String getUsername() {        return username;    }    @Override    public boolean isAccountNonExpired() {        return true;    }    @Override    public boolean isAccountNonLocked() {        return !locked;    }    @Override    public boolean isCredentialsNonExpired() {        return true;    }    @Override    public boolean isEnabled() {        return enabled;    }    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public void setUsername(String username) {        this.username = username;    }    public void setPassword(String password) {        this.password = password;    }    public Boolean getEnabled() {        return enabled;    }    public void setEnabled(Boolean enabled) {        this.enabled = enabled;    }    public Boolean getLocked() {        return locked;    }    public void setLocked(Boolean locked) {        this.locked = locked;    }    public List getRoleList() {        return roleList;    }    public void setRoleList(List roleList) {        this.roleList = roleList;    }}
public class Role {    private Integer id;    private String name;    private String nameZh;...}
public class Menu {    private Integer id;    private String pattern;    private List roles;...}

创建UserMapper类&&UserMapper.xml

和MenuMapper类&&MenuMapperxml

UserMapper

@Mapperpublic interface UserMapper {    User getUserByName(String name);    List getRoleById(Integer id);}

UserMapper.xml

        

MenuMapper

@Mapperpublic interface MenuMapper {    List getMenus();}

MemuMapper.xml

                                                                                

创建UserService MenuService

创建UserService实现UserServiceDetails接口

@Servicepublic class UserService implements UserDetailsService {    @Autowired    private UserMapper userMapper;    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        User user = userMapper.getUserByName(username);        if(user ==null){            throw new UsernameNotFoundException("用户名不存在");        }        user.setRoleList(userMapper.getRoleById(user.getId()));        return user;    }}

创建MenuService

@Servicepublic class MenuService {    @Autowired    private MenuMapper menuMapper;    public List getMenus() {return menuMapper.getMenus();}}

创建CustomFilterInvocationSecurityMetadataSource

实现接口FilterInvocationSecurityMetadataSource

注:加@comppent注解,把自定义类注册成spring组件

supports返回值设成true表示支持

重写getAttributes()方法

  • invacation 调用 ,求助

  • metadata 元数据

@Componentpublic class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {    //ant风格的路径匹配器    AntPathMatcher pathMatcher = new AntPathMatcher();    @Autowired    private MenuService menuService;        //supports返回值设成true表示支持    @Override    public boolean supports(Class aClass) {        return true;    }    @Override    public Collection getAttributes(Object object) throws IllegalArgumentException {        //获取当前用户请求的url        String requestUrl=((FilterInvocation) object).getRequestUrl();        //数据库中查询出所有的路径        List menus  =menuService.getMenus();        for (Menu menu : menus) {            //判断用户请求的url和数据库的url是否能匹配的上            if (pathMatcher.match(menu.getPattern(), requestUrl)) {                List roles =menu.getRoles();                String[] roleStr = new String[roles.size()];                for (int i = 0; i < roles.size(); i++) {                    roleStr[i]=roles.get(i).getName();                }                //将筛选的url路径所具备的角色返回回去                return SecurityConfig.createList(roleStr);            }        }        //如果没有匹配上就返回一个默认的角色,作用好比作一个标记        return SecurityConfig.createList("ROLE_def");    }    @Override    public Collection getAllConfigAttributes() {        return null;    }}

创建CustomAccessDecisionManager

实现AccessDecisionManager接口 access 通道

注:加@comppent注解,把自定义类注册成spring组件

将两个supports()都设置成true

重写decide()方法

@Componentpublic class CustomAccessDecisionManager implements AccessDecisionManager {    @Override    public void decide(Authentication authentication, Object o, Collection collection) throws AccessDeniedException, InsufficientAuthenticationException {        //configattributes里存放着CustomFilterInvocationSecurityMetadataSource过滤出来的角色        for (ConfigAttribute configAttribute : collection) {            //如果你请求的url在数据库中不具备角色            if ("ROLE_def".equals(configAttribute.getAttribute())) {                //在判断是不是匿名用户(也就是未登录)                if (authentication instanceof AnonymousAuthenticationToken) {                    System.out.println(">>>>>>>>>>>>>>>>匿名用户>>>>>>>>>>>>>>");                    throw new AccessDeniedException("权限不足,无法访问");                }else{                    //这里面就是已经登录的其他类型用户,直接放行                    System.out.println(">>>>>>>>>>>其他类型用户>>>>>>>>>>>");                    return;                }            }            //如果你访问的路径在数据库中具有角色就会来到这里            //Autherntication这里面存放着登录后的用户所有信息            Collection authorities = authentication.getAuthorities();            for (GrantedAuthority authority : authorities) {                System.out.println(">>>>>>>authority(账户所拥有的权限):"+authority.getAuthority());                System.out.println(">>>>>>>configAttribute(路径需要的角色):"+configAttribute.getAttribute());                //路径需要的角色和账户所拥有的角色作比较                if (authority.getAuthority().equals(configAttribute.getAttribute())) {                    System.out.println(">>>>>>>>>>>>>>>>>>进来>>>>>>>>>>>>>>>>>");                    return;                }            }        }    }    @Override    public boolean supports(ConfigAttribute configAttribute) {        return true;    }    @Override    public boolean supports(Class aClass) {        return true;    }}

创建WebSecurityConfig配置类

WebSecurityConfig实现WebSecurityConfigurerAdapter

注入一会所需要的类

SpringSecurity5.0之后必须密码加密

将数据库查出的账户密码交给SpringSecurity去判断

配置HttpSecurity

@Configurationpublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {    @Autowired    private UserService userService;    @Autowired    private CustomFilterInvocationSecurityMetadataSource customFilterInvocationSecurityMetadataSource;    @Autowired    private CustomAccessDecisionManager customAccessDecisionManager;    //springSecutity5.0之后必密码加密    @Bean    PasswordEncoder passwordEncoder(){        return new BCryptPasswordEncoder();    }    //将数据库查出的账户密码交给springsecurity去判断    @Override    protected void configure(AuthenticationManagerBuilder auth) throws Exception {        auth.userDetailsService(userService);    }    //配置HttpSecurity    @Override    protected void configure(HttpSecurity http) throws Exception {        http.authorizeRequests()                .withObjectPostProcessor(new ObjectPostProcessor() {                    @Override                    public  O postProcess(O object){                        object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);                        object.setAccessDecisionManager(customAccessDecisionManager);                        return object;                    }                })                .and()                .formLogin()                .permitAll()                .and()                .csrf().disable();    }}

Controller

@RestControllerpublic class HelloController {    @GetMapping("/hello")    public String hello(){        return "hello";    }    @GetMapping("/dba/hello")    public String dba(){        return "hello dba";    }    @GetMapping("/admin/hello")    public String admin(){        return "hello admin";    }    @GetMapping("/user/hello")    public String user(){        return "hello user";    }}

以上是"如何解决Spring Security的权限配置不生效问题"这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注行业资讯频道!

0