美文网首页
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