zoukankan      html  css  js  c++  java
  • springsecurity 权限管理实例菜单管理

    项目需求

    根据用户权限不同显示不同菜单,admin显示菜单(用户管理、角色管理、菜单管理),guest显示菜单(用户管理、菜单管理)

    显示菜单原理

    向客户端返回不同的菜单数据

    需求分析

    数据结构:用户表、角色表、用户角色关系表、菜单表、菜单角色关系表。

    项目业务逻辑:通过登录用户名获取用户对象,并根据用户对象的Id属性获取菜单信息

    技术选型

    springboot:主要节省配置时间

    springsecurity:用于认证和授权

    MySQL:关系型数据管理

    mybatis-plus(或mybatis):为了使用自动生成实体类

    Lombok:结合mybatis-plus,免去手动编写实体类的get、set方法

    开发前期

    数据准备

    CREATE TABLE `t_menu` (
      `id` INT NOT NULL AUTO_INCREMENT,
      `name` VARCHAR(30) DEFAULT NULL,
      `href` VARCHAR(50) DEFAULT NULL,
      `isEnable` TINYINT(1) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3;
    
    /*Data for the table `t_menu` */
    
    INSERT  INTO `t_menu`(`id`,`name`,`href`,`isEnable`) VALUES 
    (1,'用户管理','users',1),
    (2,'角色管理','roles',1),
    (3,'菜单管理','menus',1);
    
    /*Table structure for table `t_role` */
    
    CREATE TABLE `t_role` (
      `id` INT NOT NULL AUTO_INCREMENT,
      `role` VARCHAR(50) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3;
    
    /*Data for the table `t_role` */
    
    INSERT  INTO `t_role`(`id`,`role`) VALUES 
    (1,'管理员'),
    (2,'宾客');
    
    /*Table structure for table `t_role_menu` */
    
    CREATE TABLE `t_role_menu` (
      `role_Id` INT NOT NULL,
      `menu_Id` INT NOT NULL,
      PRIMARY KEY (`role_Id`,`menu_Id`)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8mb3;
    
    /*Data for the table `t_role_menu` */
    
    INSERT  INTO `t_role_menu`(`role_Id`,`menu_Id`) VALUES 
    (1,1),
    (1,2),
    (1,3),
    (2,1),
    (2,3);
    
    /*Table structure for table `t_role_user` */
    
    CREATE TABLE `t_role_user` (
      `user_Id` INT NOT NULL,
      `role_Id` INT NOT NULL,
      PRIMARY KEY (`user_Id`,`role_Id`)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8mb3;
    
    /*Data for the table `t_role_user` */
    
    INSERT  INTO `t_role_user`(`user_Id`,`role_Id`) VALUES 
    (1,1),
    (2,2);
    
    /*Table structure for table `t_user` */
    
    CREATE TABLE `t_user` (
      `id` INT NOT NULL AUTO_INCREMENT,
      `username` VARCHAR(50) DEFAULT NULL,
      `password` VARCHAR(20) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3;
    
    /*Data for the table `t_user` */
    
    INSERT  INTO `t_user`(`id`,`username`,`password`) VALUES 
    (1,'admin','123'),
    (2,'guest','123');
    View Code

    添加依赖

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.2.0</version>
            </dependency>
    
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-generator</artifactId>
                <version>3.4.1</version>
            </dependency>
    
            <dependency>
                <groupId>org.freemarker</groupId>
                <artifactId>freemarker</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    View Code

    使用mybatis-plus-generator生成基础架构

    这个代码不是项目代码所以放在了test下

    public class CodeGenerator {
    
        // 数据库 URL
        private static final String URL = "jdbc:mysql://192.168.223.129:3306/springbootdb?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC";
        // 数据库驱动
        private static final String DRIVER_NAME = "com.mysql.cj.jdbc.Driver";
        // 数据库用户名
        private static final String USERNAME = "root";
        // 数据库密码
        private static final String PASSWORD = "root";
        // @author 值
        private static final String AUTHOR = "marw";
        // 包的基础路径
        private static final String BASE_PACKAGE_URL = "com.marw.sys";
        // xml文件路径
        private static final String XML_PACKAGE_URL = "/src/main/resources/mapper/";
        // xml 文件模板
        private static final String XML_MAPPER_TEMPLATE_PATH = "/templates/mapper.xml";
        // mapper 文件模板
        private static final String MAPPER_TEMPLATE_PATH = "/templates/mapper.java";
        // entity 文件模板
        private static final String ENTITY_TEMPLATE_PATH = "/templates/entity.java";
        // service 文件模板
        private static final String SERVICE_TEMPLATE_PATH = "/templates/service.java";
        // serviceImpl 文件模板
        private static final String SERVICE_IMPL_TEMPLATE_PATH = "/templates/serviceImpl.java";
        // controller 文件模板
        private static final String CONTROLLER_TEMPLATE_PATH = "/templates/controller.java";
    
        public static void main(String[] args) {
            AutoGenerator generator = new AutoGenerator();
    
            // 全局配置
            GlobalConfig globalConfig = new GlobalConfig();
            String projectPath = System.getProperty("user.dir");
            globalConfig.setOutputDir(projectPath + "/src/main/java");
            globalConfig.setAuthor(AUTHOR);
            globalConfig.setOpen(false);
            globalConfig.setFileOverride(false);
            generator.setGlobalConfig(globalConfig);
    
            // 数据源配置
            DataSourceConfig dataSourceConfig = new DataSourceConfig();
            dataSourceConfig.setUrl(URL);
            dataSourceConfig.setDriverName(DRIVER_NAME);
            dataSourceConfig.setUsername(USERNAME);
            dataSourceConfig.setPassword(PASSWORD);
            generator.setDataSource(dataSourceConfig);
    
            // 包配置
            PackageConfig packageConfig = new PackageConfig();
            //packageConfig.setModuleName("gen");
            packageConfig.setParent(BASE_PACKAGE_URL);
            generator.setPackageInfo(packageConfig);
    
    
            // 自定义配置
            InjectionConfig cfg = new InjectionConfig() {
                @Override
                public void initMap() {
                    // to do nothing
                }
            };
            // 如果模板引擎是 freemarker
            String templatePath = "/templates/mapper.xml.ftl";
    
            // 自定义输出配置
            List<FileOutConfig> focList = new ArrayList<>();
            // 自定义配置会被优先输出
            focList.add(new FileOutConfig(templatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
                    return projectPath + XML_PACKAGE_URL + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
                }
            });
    
            cfg.setFileOutConfigList(focList);
            generator.setCfg(cfg);
    
            // 配置自定义代码模板
            TemplateConfig templateConfig = new TemplateConfig();
            templateConfig.setXml(null);
    
    
    
    
            templateConfig.setMapper(MAPPER_TEMPLATE_PATH);
            templateConfig.setEntity(ENTITY_TEMPLATE_PATH);
            templateConfig.setService(SERVICE_TEMPLATE_PATH);
            templateConfig.setServiceImpl(SERVICE_IMPL_TEMPLATE_PATH);
            templateConfig.setController(CONTROLLER_TEMPLATE_PATH);
            generator.setTemplate(templateConfig);
    
            // 策略配置
            StrategyConfig strategy = new StrategyConfig();
            strategy.setNaming(NamingStrategy.underline_to_camel);
            strategy.setColumnNaming(NamingStrategy.underline_to_camel);
            strategy.setEntityLombokModel(true);
            strategy.setRestControllerStyle(true);
            //strategy.setInclude(scanner("表名"));
            strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
            //strategy.setSuperEntityColumns("id");
            //驼峰转换
            strategy.setControllerMappingHyphenStyle(true);
            //strategy.setTablePrefix(packageConfig.getModuleName() + "_");
            strategy.setTablePrefix("t_");
            generator.setStrategy(strategy);
            generator.setTemplateEngine(new FreemarkerTemplateEngine());
            generator.execute();
        }
    
        private static String scanner(String tip) {
            Scanner scanner = new Scanner(System.in);
            System.out.println(("请输入" + tip + ":"));
            if (scanner.hasNext()) {
                String ipt = scanner.next();
                if (StringUtils.isNotBlank(ipt)) return ipt;
            }
            throw new MybatisPlusException("请输入正确的" + tip + "!");
        }
    }
    View Code

    生成后的结果也做了修改,效果如下:

    基本开发

    Mapper

    根据用户名获取用户信息

    <?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.marw.sys.mapper.UserMapper">
        <select id="findUserByUsername" resultType="com.marw.sys.entity.User">
            select * from t_user where username=#{username}
        </select>
    </mapper>
    View Code

    根据用户Id获取菜单信息,菜单中需要角色信息

    @Data
    public class MenuExtend extends Menu{
        private String roleId;
        private String roleName;
    }
    <?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.marw.sys.mapper.MenuMapper">
        <select id="findMenuByUsreId" resultType="com.marw.sys.entity.MenuExtend">
            select tm.*,r.id roleId,r.role roleName
            from t_menu tm
                inner join t_role_menu trm on tm.id=trm.menu_Id
                inner join t_role r on trm.role_id=r.id
                inner join t_role_user ru on trm.role_Id=ru.role_Id
            where ru.user_Id=#{id}
        </select>
    </mapper>

    application.yaml

     

    server:
      port: 8083
    
    spring:
      datasource:
        username: root
        password: root
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.223.129:3306/springbootdb?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useAffectedRows=true
    
    mybatis-plus:
      type-aliases-package: com.marw.domain
      mapper-locations: classpath:mapper/*.xml
      configuration:
        jdbc-type-for-null: null
      global-config:
        banner: false
    View Code

    修改springboot启动类

    添加Mapper包扫描

    @SpringBootApplication
    @MapperScan("com.marw.sys.mapper")
    public class SpringSecurityWebApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringSecurityWebApplication.class, args);
        }
    
    }
    View Code

    认证开发

    springsecurity认证,通过实现UserDetailsService接口中loadUserByUsername方法从数据库获取数据对用户进行认证,认证结果存储在实现UserDetails接口的对象中,其对象需要包含了用户信息和菜单信息(菜单信息是我们需要的)

    UserDetails实现类SecurityUser

    public class SecurityUser implements UserDetails {
    
        private User user;
        private List<MenuExtend> menuList;
    
        public User getUser() {
            return user;
        }
    
        public List<MenuExtend> getMenuList() {
            return menuList;
        }
    
        public SecurityUser(User user, List<MenuExtend> menuList) {
            this.user = user;
            this.menuList = menuList;
        }
    
        /**
         * 菜单中需要角色信息就是为了这个方法
         * @return
         */
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            Collection<GrantedAuthority> authorities = new ArrayList<>();
            for (MenuExtend item : this.menuList) {
                SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(item.getRoleName());
                authorities.add(simpleGrantedAuthority);
            }
            return authorities;
        }
    
        @Override
        public String getPassword() {
            return user.getPassword();
        }
    
        @Override
        public String getUsername() {
            return user.getUsername();
        }
    
        @Override
        public boolean isAccountNonExpired() {
            return true;//数据库中没有这个字段直接设置true
        }
    
        @Override
        public boolean isAccountNonLocked() {
            return true;//数据库中没有这个字段直接设置true
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return true;//数据库中没有这个字段直接设置true
        }
    
        @Override
        public boolean isEnabled() {
            return true;//数据库中没有这个字段直接设置true
        }
    }
    View Code

    UserDetailsService实现类UserDetailsServiceImpl

    @Component
    public class UserDetailsServiceImpl implements UserDetailsService {
    
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        @Autowired
        private UserMapper userMapper;
        @Autowired
        private MenuMapper menuMapper;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            //根据username获取user信息
            com.marw.sys.entity.User user=userMapper.findUserByUsername(username);
    
            if(user==null){
                throw new UsernameNotFoundException("用户不存在");
            }
    
            user.setPassword(passwordEncoder.encode(user.getPassword()));
            //根据UserID获取菜单
            List<MenuExtend> menuList = menuMapper.findMenuByUsreId(user.getId());
    
            SecurityUser securityUser = new SecurityUser(user, menuList);
            return securityUser;
        }
    }
    View Code

    设置认证

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private UserDetailsService userDetailsServiceImpl;
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            //指定认证和加密方式
            auth.userDetailsService(userDetailsServiceImpl).passwordEncoder(passwordEncoder());
        }
    
        @Bean
        public PasswordEncoder passwordEncoder(){
            return new BCryptPasswordEncoder();
        }
    }

    测试Controller

    获取springsecurity认证的数据是通过SecurityContextHolder获取的

    @RestController
    public class IndexController {
        @GetMapping("/index")
        public String index(){
            return "index";
        }
    
        @GetMapping("/permission")
        @ResponseBody
        public Map<String,Object> getPermisiion(){
            SecurityUser user = (SecurityUser)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            List<MenuExtend> menuList = user.getMenuList();
            Map<String,Object> data = new HashMap<>();
            data.put("list",menuList);
            return data;
        }
    }

    访问http://localhost:8083/permission,会跳转到登陆页面

    通过数据库中的用户登录就可以得到结果:

  • 相关阅读:
    fastjson的@JSONField注解
    Java 日期比较大小
    linux 查看文件显示行号
    Java double 加、减、乘、除
    Java 身份证判断性别获取年龄
    linux 查看端口被占用
    Unable to complete the scan for annotations for web application [/wrs] due to a StackOverflowError. Possible root causes include a too low setting for -Xss and illegal cyclic inheritance dependencies.
    nginx 返回数据不完整
    linux redis 启动 overcommit_memory
    IntelliJ IDEA 常用设置
  • 原文地址:https://www.cnblogs.com/WarBlog/p/15157431.html
Copyright © 2011-2022 走看看