美文网首页
spring security动态配置url权限

spring security动态配置url权限

作者: 程序员小杰 | 来源:发表于2020-02-03 16:59 被阅读0次

    在前面的博文中权限地址我是在代码中写死的,在实际开发中肯定不行。这篇博文实现动态配置url权限。
    脚本

    CREATE TABLE `menu`  (
      `id` int(11) NOT NULL,
      `path` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
    
    INSERT INTO `menu` VALUES (1, '/admin/**');
    INSERT INTO `menu` VALUES (2, '/dba/**');
    INSERT INTO `menu` VALUES (3, '/user/**');
    
    CREATE TABLE `menu_role`  (
      `id` int(11) NOT NULL,
      `mid` int(11) DEFAULT NULL COMMENT '菜单id',
      `rid` int(11) DEFAULT NULL COMMENT '角色id',
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
    
    INSERT INTO `menu_role` VALUES (1, 1, 1);
    INSERT INTO `menu_role` VALUES (2, 2, 1);
    INSERT INTO `menu_role` VALUES (3, 1, 2);
    INSERT INTO `menu_role` VALUES (4, 3, 3);
    

    新增两张表,一张是菜单表,还有一张是菜单权限表,其他表请看我上一篇博文https://www.jianshu.com/p/ec5556e05cf3
    实体:
    Menu

    import java.util.List;
    
    public class Menu {
    
        private Integer id;
        private String path;
        private List<Role> roles;   //该菜单允许什么角色访问
        
        
        public List<Role> getRoles() {
            return roles;
        }
        public void setRoles(List<Role> roles) {
            this.roles = roles;
        }
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getPath() {
            return path;
        }
        public void setPath(String path) {
            this.path = path;
        }   
    }
    
    

    MenuService

    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.gongj.secuitydb.entity.Menu;
    import com.gongj.secuitydb.mapper.MenuMapper;
    
    @Service
    public class MenuService {
    
        @Autowired
        MenuMapper menuMapper;
        
        public List<Menu> getAllMenu(){
            return menuMapper.getAllMenu();
        }
    }
    
    

    MenuMapper

    import java.util.List;
    
    import com.gongj.secuitydb.entity.Menu;
    
    public interface MenuMapper {
    
        List<Menu> getAllMenu();
    
    }
    

    MenuMapper.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.gongj.secuitydb.mapper.MenuMapper">
    
    <resultMap type="com.gongj.secuitydb.entity.Menu" id="BaseResultMap">
        <id column="id" property="id"></id>
        <result column="path" property="path"/>
        <collection property="roles" ofType="com.gongj.secuitydb.entity.Role">
            <id column="id" property="id"/>
            <result column="rnameEn" property="nameEN"/>
            <result column="rnameZh" property="nameZh"/>
        </collection>
    </resultMap>
    <select id="getAllMenu" resultMap="BaseResultMap">
    select m.*,r.id as rid,r.nameZh as rnameZh,r.nameEn as rnameEn from menu m left join menu_role mr
     on m.id = mr.mid left join role r on  r.id = mr .rid
    </select>
    
    </mapper>
    

    自定义FilterInvocationSecurityMetadataSource

    import java.util.Collection;
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.access.ConfigAttribute;
    import org.springframework.security.access.SecurityConfig;
    import org.springframework.security.web.FilterInvocation;
    import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
    import org.springframework.stereotype.Component;
    import org.springframework.util.AntPathMatcher;
    
    import com.gongj.secuitydb.entity.Menu;
    import com.gongj.secuitydb.entity.Role;
    import com.gongj.secuitydb.service.MenuService;
    
    @Component
    public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource{
    
        AntPathMatcher pathMatcher = new AntPathMatcher();
        
        @Autowired
        MenuService menuService;
        @Override
        public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
            String requestUrl = ((FilterInvocation)o).getRequestUrl(); //用户请求地址
            List<Menu> allMenu = menuService.getAllMenu(); //所有菜单
            for (Menu menu : allMenu) {
                //进行匹配,如果匹配成功,就返回该请求需要的角色
                if(pathMatcher.match(menu.getPath(), requestUrl)) {
                    List<Role> roles = menu.getRoles();
                    String[] rolesStr = new String[roles.size()];
                    for (int i = 0; i < roles.size(); i++) {
                        rolesStr[i] = roles.get(i).getNameEN();
                    }
                    return SecurityConfig.createList(rolesStr);
                }
            }
            //如果没有匹配成功,就说明该请求不需要拥有角色就能访问,给它一个标识符。
            return SecurityConfig.createList("ROLE_LOGIN");
        }
    
        @Override
        public Collection<ConfigAttribute> getAllConfigAttributes() {
            return null;
        }
    
        @Override
        public boolean supports(Class<?> clazz) {
            return true;
        }
    
    }
    

    自定义AccessDecisionManager

    import java.util.Collection;
    
    import org.springframework.security.access.AccessDecisionManager;
    import org.springframework.security.access.AccessDeniedException;
    import org.springframework.security.access.ConfigAttribute;
    import org.springframework.security.authentication.AnonymousAuthenticationToken;
    import org.springframework.security.authentication.InsufficientAuthenticationException;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.stereotype.Component;
    
    @Component
    public class CustomAccessDecisionManager implements AccessDecisionManager{
    
        @Override
        public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
                throws AccessDeniedException, InsufficientAuthenticationException {
                for (ConfigAttribute configAttribute : configAttributes) {
                    if("ROLE_LOGIN".equals(configAttribute.getAttribute())) { //拥有标识符的接口需要进行登录才能访问
                        if(authentication instanceof AnonymousAuthenticationToken) {  //说明你是匿名用户,没登录
                            throw new AccessDeniedException("请登录");
                        }else {
                            return;
                        }
                    }
                    Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
                    for (GrantedAuthority authoritie : authorities) {
                        if(authoritie.getAuthority().equals(configAttribute.getAttribute())) {
                            return;
                        }
                    }
                }
                throw new AccessDeniedException("非法请求");
        }
    
        @Override
        public boolean supports(ConfigAttribute attribute) {
            // TODO Auto-generated method stub
            return true;
        }
    
        @Override
        public boolean supports(Class<?> clazz) {
            // TODO Auto-generated method stub
            return true;
        }
    
    }
    

    SecurityConfig

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.ObjectPostProcessor;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
    
    import com.gongj.secuitydb.service.UserSerivice;
    
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter{
    
        @Autowired
        UserSerivice userService;
    
        @Autowired
        CustomAccessDecisionManager customAccessDecisionManager;
        
        @Autowired
        CustomFilterInvocationSecurityMetadataSource customFilterInvocationSecurityMetadataSource;
        
        @Bean
        PasswordEncoder PasswordEncoder() {
            return new BCryptPasswordEncoder();
        }
        
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userService);
        }
        
            
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
            .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
    
                @Override
                public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                    o.setAccessDecisionManager(customAccessDecisionManager);
                    o.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);
                    return o;
                }
            })
            .and()
            .formLogin()
            .permitAll()
            .and()
            .csrf().disable();
        }
    }
    
    

    创建Controller进行测试

    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class SecurityController {
    
        @GetMapping("/he")
        public String he() {
            return "hello  Security";
        }
        
        @GetMapping("/admin/he")
        public String admin() {
            return "hello  admin";
        }
        
        
        @GetMapping("/dba/he")
        public String dba() {
            return "hello  db";
        }
        
        @GetMapping("/user/he")
        public String user() {
            return "hello  user";
        }
    }
    

    访问http://localhost:8081/he来到登录页,使用root用户登录

    image.png
    根据我们创建的表,可以得出root用户的角色是ROLE_dba和ROLE_admin,该角色拥有/admin/和/dba/菜单的访问权限,不能访问/user/**菜单。
    访问http://localhost:8081/admin/he成功,可以进行访问。
    image.png
    访问http://localhost:8081/user/he访问user菜单下的接口是不能进行访问的, image.png

    相关文章

      网友评论

          本文标题:spring security动态配置url权限

          本文链接:https://www.haomeiwen.com/subject/donhxhtx.html