shiro

最近在学shiro以及mybatisplus代码生成器,在搭建过程中多多少少也踩了一些坑。留作纪念吧。

shiro

img

Shiro是一个强大的简单易用的Java安全框架,主要用来更便捷的认证,授权,加密,会话管理。可以看出shiro除了基本的认证,授权,会话管理,加密之外,还有许多额外的特性。

shiro主要架构

img

​ subject:一般是用户,任何与应用交互的“用户”。

​ securitymanager:安全管理器(管理realms,sessionmanager,ssiondao,cachemanager),shiro的核心。

​ realms:Realms作为Shiro和你的应用的连接桥,当需要与安全数据交互的时候,像用户账户,或者访问控制,Shiro就从一个或多个Realms中查找。

搭建步骤

  1. 导入shiro依赖

    因为使用了redis存放sessionId所以也导入了shiro-redis依赖。

    <!--        shiro-->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.3.2</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>1.3.2</version>
            </dependency>
    <!--        shiro redis-->
            <dependency>
                <groupId>org.crazycake</groupId>
                <artifactId>shiro-redis</artifactId>
                <version>3.0.0</version>
            </dependency>
  2. 配置shiroConfiguration

    这是shiro的配置类,使用 LifecycleBeanPostProcessor Bean会在创建sessionid时报null异常,注释掉发现对权限注解的支持没有影响。

    package hnu.boot.config;
    
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.spring.LifecycleBeanPostProcessor;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.crazycake.shiro.RedisCacheManager;
    import org.crazycake.shiro.RedisManager;
    import org.crazycake.shiro.RedisSessionDAO;
    import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @Classname ShiroConfiguration
     * @Description TODO
     * @Date 2019/10/23 15:32
     * @Created by yz
     */
    
    @Configuration
    public class ShiroConfiguration {
        //1.realm
        @Bean
        public CustomRealm getRealm(){
            return new CustomRealm();
        }
        //2.securityManagerment
        @Bean
        public SecurityManager getSecurityManager(CustomRealm realm){
            DefaultSecurityManager securityManager=new DefaultWebSecurityManager();
            securityManager.setRealm(realm);
            //将自定义的sessionmanager注册到securitymanager中
            securityManager.setSessionManager(sessionManager());
            //将自定义的cachemanager注册到securitymanager
            securityManager.setCacheManager(redisCacheManager());
            return securityManager;
        }
        //3.shiro过滤器工厂
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
            ShiroFilterFactoryBean filterFactoryBean=new ShiroFilterFactoryBean();
            filterFactoryBean.setSecurityManager(securityManager);
            filterFactoryBean.setLoginUrl("/autherror?code=1");
            filterFactoryBean.setUnauthorizedUrl("/autherror?code=2");
    
            Map<String,String> filterMap=new HashMap<>();
            //当前请求可以匿名访问
            filterMap.put("/home","anon");
            //认证后才能访问
            filterMap.put("/user/**","authc");
            filterFactoryBean.setFilterChainDefinitionMap(filterMap);
            return filterFactoryBean;
        }
    
        @Value("${spring.redis.host}")
        private String host;
    
        @Value("${spring.redis.port}")
        private int port;
    
    
        /**
         * redis控制器 操作redis
         * @return
         */
    
        public RedisManager redisManager(){
            RedisManager redisManager=new RedisManager();
            redisManager.setHost(host);
            redisManager.setPort(port);
            return redisManager;
        }
    
        /** sessionDao*/
        public RedisSessionDAO redisSessionDAO(){
            RedisSessionDAO redisSessionDAO=new RedisSessionDAO();
            redisSessionDAO.setRedisManager(redisManager());
            return redisSessionDAO;
        }
    
        /**
         * sessionManager
         * @return
         */
        public DefaultWebSessionManager sessionManager(){
            CustomSessionManager sessionManager=new CustomSessionManager();
            sessionManager.setSessionDAO(redisSessionDAO());
    
            return sessionManager;
        }
    
        /**
         * 缓存管理器cacha manager (redis)
         * @return
         */
        public RedisCacheManager redisCacheManager(){
            RedisCacheManager cacheManager=new RedisCacheManager();
            cacheManager.setRedisManager(redisManager());
            return cacheManager;
        }
        
        //4.开启对注解的支持
    
    //    @Bean
    //    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
    //        return new LifecycleBeanPostProcessor();
    //    }
    
    //    @DependsOn({"lifecycleBeanPostProcessor"})
        @Bean
        public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
            DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
            advisorAutoProxyCreator.setProxyTargetClass(true);
            return advisorAutoProxyCreator;
        }
    
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
            AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
            advisor.setSecurityManager(securityManager);
            return advisor;
        }
    
    }
  3. 写realm

    其实就是重写认证与授权方法。

    package hnu.boot.config;
    
    
    import com.baomidou.mybatisplus.mapper.EntityWrapper;
    import hnu.boot.readboy.entity.User;
    import hnu.boot.readboy.service.impl.UserServiceImpl;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.crypto.hash.Md5Hash;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     * 自定义的realm
     */
    public class CustomRealm extends AuthorizingRealm {
    
        @Override
        public void setName(String name) {
            super.setName("customRealm");
        }
    
        @Autowired
        private UserServiceImpl userService;
    
        /**
         * 权限认证
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
           
            User user = (User) principalCollection.getPrimaryPrincipal();//得到唯一的安全数据
            
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            Set<String> roles = new HashSet<>();
            Set<String> perms = new HashSet<>();
    
            if (user.getId().equals(1)){
                roles.add("admin");
            }
    
            roles.add("user");
    
            info.setStringPermissions(perms);
            info.setRoles(roles);
            return info;
        }
    
    
        /**
         * 登录校验
         * 
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
           
            UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;
            String username = upToken.getUsername();
            String password = new String( upToken.getPassword());
            
            User user = userService.selectOne(new EntityWrapper<User>().eq("username",username));
            
            if(user != null && user.getPassword().equals(password)) {
                SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
                return info;
            }
            
            return null;
        }
    
    }

4.测试权限校验

package hnu.boot.readboy.controller;


import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresGuest;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author yzw
 * @since 2019-10-23
 */
@Slf4j
@RestController
public class UserController {

    @RequestMapping(value = "/login",method = RequestMethod.GET)
    public String login(String username,String password){
        try {
            password=new Md5Hash(password,username,3).toString();
            UsernamePasswordToken token=new UsernamePasswordToken(username,password);
            Subject subject=SecurityUtils.getSubject();
            String sid= (String) subject.getSession().getId();
            subject.login(token);
            return "success "+sid;
        }catch (Exception e){
            e.printStackTrace();
            return "error.";
        }
    }

    @RequestMapping(value = "/home",method = RequestMethod.GET)
    @RequiresRoles("user")
    public String home(){
        Subject subject=SecurityUtils.getSubject();
        subject.getSession();
        return "home";
    }

    @RequestMapping(value = "/user/admin",method = RequestMethod.GET)
    @RequiresRoles("admin")
    public String Admin(){
        return "admin";
    }
    @RequestMapping(value = "/autherror")
    public String autherror(int code){
        return code==1?"还未登录":"无权限";
    }
}

mybatis plus代码生成器

  1. 导入坐标

     <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>2.2.0</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-generator</artifactId>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-core</artifactId>
                <version>2.2.0</version>
            </dependency>
            <dependency>
                <groupId>org.apache.velocity</groupId>
                <artifactId>velocity-engine-core</artifactId>
                <version>2.1</version>
            </dependency>
            <dependency>
                <groupId>org.freemarker</groupId>
                <artifactId>freemarker</artifactId>
                <version>2.3.29</version>
            </dependency>
            <dependency>
                <groupId>com.ibeetl</groupId>
                <artifactId>beetl</artifactId>
                <version>3.0.13.RELEASE</version>
            </dependency>
    

    实际使用过程中貌似要用3.+版本。这里使用2.+是因为可以使用entitywrapper。

  2. 修改application.yml

    spring:
      application:
        name: HNUSBoot
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://*******/****?userUnicode=true&characterEncoding=UTF8&useSSL=false&useAffectedRows=true&useLegacyDatetimeCode=false&serverTimezone=CTT&useJDBCCompliantTimezoneShift=true
        username: readboy
        password: ******
      redis:
        host: *******
        port: 6379
    mybatis-plus:
      mapper-locations: classpath*:xml/*.xml
      global-config:
        id-type: 3
    
  3. 编写CodeGenerator(官方例子)

    package hnu.boot.config;
    
    
    import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
    import com.baomidou.mybatisplus.generator.AutoGenerator;
    import com.baomidou.mybatisplus.generator.InjectionConfig;
    import com.baomidou.mybatisplus.generator.config.*;
    import com.baomidou.mybatisplus.generator.config.po.TableInfo;
    import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
    import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
    import org.apache.commons.lang3.StringUtils;
    
    import javax.xml.crypto.Data;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Scanner;
    
    /**
     * @Classname CodeGenerator
     * @Description TODO
     * @Date 2019/10/22 22:21
     * @Created by yz
     */
    // 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
    public class CodeGenerator {
    
        private static String URL="jdbc:mysql:xxxx";
        private static String USER_NAME="readboy";
        private static String PASS_WORD="xxxxxxxx";
        private static String DRIVER="com.mysql.cj.jdbc.Driver";
    
        /**
         * <p>
         * 读取控制台内容
         * </p>
         */
        public static String scanner(String tip) {
            Scanner scanner = new Scanner(System.in);
            StringBuilder help = new StringBuilder();
            help.append("请输入" + tip + ":");
            System.out.println(help.toString());
            if (scanner.hasNext()) {
                String ipt = scanner.next();
                if (StringUtils.isNotEmpty(ipt)) {
                    return ipt;
                }
            }
            throw new MybatisPlusException("请输入正确的" + tip + "!");
        }
    
        public static void main(String[] args) {
            // 代码生成器
            AutoGenerator mpg = new AutoGenerator();
    
            // 全局配置
            GlobalConfig gc = new GlobalConfig();
            //项目路径
            String projectPath = "E:/chenxuyuan/IDeaPj/boot";
            gc.setOutputDir(projectPath + "/src/main/java");
            gc.setAuthor("yzw");
            gc.setOpen(false);
            // gc.setSwagger2(true); 实体属性 Swagger2 注解
            mpg.setGlobalConfig(gc);
    
            // 数据源配置
            DataSourceConfig dsc = new DataSourceConfig();
            dsc.setUrl(URL);
            // dsc.setSchemaName("public");
            dsc.setDriverName(DRIVER);
            dsc.setUsername(USER_NAME);
            dsc.setPassword(PASS_WORD);
            mpg.setDataSource(dsc);
    
            // 包配置
            PackageConfig pc = new PackageConfig();
            pc.setModuleName(scanner("模块名"));
            pc.setParent("hnu.boot");
            mpg.setPackageInfo(pc);
    
            // 自定义配置
            InjectionConfig cfg = new InjectionConfig() {
                @Override
                public void initMap() {
                    // to do nothing
                }
            };
    
            // 如果模板引擎是 freemarker
            String templatePath = "/templates/mapper.xml.ftl";
            // 如果模板引擎是 velocity
    //         String templatePath = "/templates/mapper.xml.vm";
    
            // 自定义输出配置
            List<FileOutConfig> focList = new ArrayList<>();
            // 自定义配置会被优先输出
            focList.add(new FileOutConfig(templatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!使用2.+的mybatisplus这里会报错,使用3.+就好了。。
                
                   return projectPath + "/src/main/resources/mapper/" + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
                }
            });
            /*
            cfg.setFileCreate(new IFileCreate() {
                @Override
                public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                    // 判断自定义文件夹是否需要创建
                    checkDir("调用默认方法创建的目录");
                    return false;
                }
            });
            */
            cfg.setFileOutConfigList(focList);
            mpg.setCfg(cfg);
    
            // 配置模板
            TemplateConfig templateConfig = new TemplateConfig();
    
            // 配置自定义输出模板
            //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
            // templateConfig.setEntity("templates/entity2.java");
            // templateConfig.setService();
            // templateConfig.setController();
    
            templateConfig.setXml(null);
            mpg.setTemplate(templateConfig);
    
            // 策略配置
            StrategyConfig strategy = new StrategyConfig();
            strategy.setNaming(NamingStrategy.underline_to_camel);
            strategy.setColumnNaming(NamingStrategy.underline_to_camel);
    //        strategy.setSuperEntityClass("boot.common.BaseEntity");
            strategy.setEntityLombokModel(true);
    
            // 公共父类
    //        strategy.setSuperControllerClass("boot.common.BaseController");
            // 写于父类中的公共字段
    //        strategy.setSuperEntityColumns("id");
            strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
            strategy.setControllerMappingHyphenStyle(true);
            strategy.setTablePrefix(pc.getModuleName() + "_");
            mpg.setStrategy(strategy);
            mpg.setTemplateEngine(new FreemarkerTemplateEngine());
            mpg.execute();
        }
    
    }
    

    然后执行就好啦。项目代码地址

    第一次写博客,很乱。希望对看到这篇文章的人提供一些帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值