• 1. shiro-用户认证


    shiro-用户认证

    #application.yml

    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
        username: root
        password: 12345
        type: com.alibaba.druid.pool.DruidDataSource
        druid:
          initial-size: 5 #初始连接数
          max-active: 10 #最大活动连接
          max-wait: 60000 #从池中取连接(没有闲置连接)的最大等待时间,-1表示无限等待
          min-idle: 5 #最小闲置数,小于min-idle连接池会主动创建新的连接
          time-between-eviction-runs-millis: 60000 #清理线程启动的间隔时间,当线程池中没有可用的连接启动清理线程
          min-evictable-idle-time-millis: 300000 #清理线程保持闲置最小时间
          validation-query: SELECT 1  #用于校验连接
          test-on-borrow: false #请求连接时是否校验,比较消耗性能,一般设置false
          test-on-return: false #归还连接时是否校验,比较消耗性能,一般设置false
          test-while-idle: true #清理线程通过validation-query来校验连接是否正常,如果不正常将从连接池中移除
          pool-prepared-statements: true #存储相同逻辑的sql到连接池的缓存中
          #      filters: stat,wall #监控统计web的statement(sql),以及防sql注入的wall
          # 关闭如上配置,可以采用自定义的filter
          filter:
            stat:
              enabled: true #状态监控-->stat
              db-type: mysql
              log-slow-sql: true  #记录超过指定时间的sql到日志中
              slow-sql-millis: 2000
            wall:
              enabled: true #防火墙-->wall
              db-type: mysql
              config:
                delete-allow: false #禁止删除
                drop-table-allow: false #禁止删除表
          web-stat-filter:
            enabled: true #开启监控uri,默认false
            url-pattern: /* #添加过滤规则
            exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid" #忽略过滤
          stat-view-servlet:
            enabled: true #开启视图管理界面,默认false
            url-pattern: /druid/* #视图管理界面uri
            login-username: druid #账号
            login-password: 12345 #密码
    #        allow: 127.0.0.1 白名单
    #        deny:  192.168.1.130黑名单
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启log日志
        lazy-loading-enabled: true
        aggressive-lazy-loading: false
        lazy-load-trigger-methods: ""
      global-config:
        db-config:
          logic-delete-field: deleted #逻辑删除
          logic-delete-value: 1 #已删除的值, 默认1
          logic-not-delete-value: 0 #未删除的, 默认0
    
    
    

    #mybaits-plus generator

        @Test
        public void generator() {
            //主入口函数的路径
            System.out.println(System.getProperty("user.dir"));
    
            //代码生成器
            AutoGenerator autoGenerator = new AutoGenerator();
            //全局配置 调用generator.config下的
            GlobalConfig gc = new GlobalConfig();
            //获取当前项目的路径
            String path = System.getProperty("user.dir");
            //设置是否开启AR
            gc.setAuthor("chz")
                    //文件输出路径
                    .setOutputDir(path + "/src/main/java")
                    //是否覆盖文件
                    .setFileOverride(true)
                    //设置主键自增策略
                    .setIdType(IdType.AUTO)
                    //是否开启resultMap,默认false
                    .setBaseResultMap(true)
                    //是否开启sql片段,默认false
                    .setBaseColumnList(true);
    
            //数据源配置
            DataSourceConfig dataSourceConfig = new DataSourceConfig();
            dataSourceConfig.setDbType(DbType.MYSQL)
                    .setDriverName("com.mysql.cj.jdbc.Driver")
                    .setUrl("jdbc:mysql://localhost:3306/shiro?userSSL=false&serverTimezone=Asia/Shanghai")
                    .setUsername("root")
                    .setPassword("12345");
    
            //策略配置
            StrategyConfig strategyConfig = new StrategyConfig();
            //是否开启大写命名,默认不开启
            strategyConfig.setCapitalMode(false)
                    //数据库表映射到实体类命名策略
                    .setNaming(NamingStrategy.underline_to_camel)
                    //设置想要生成的表
                    .setInclude("t_perm","t_user","t_role")
                    //生成的dao,service,entity不再带tbl_前缀
                    .setTablePrefix("t_");
    
    
            //包配置
            PackageConfig packageConfig = new PackageConfig();
            //setParent设置统一的包路径
            packageConfig.setParent("com.chz")
                    //设置包的路径
                    .setMapper("mapper")
                    .setService("service")
                    .setController("controller")
                    .setEntity("entity")
                    .setXml("mapper");
    
            //整合配置
            autoGenerator.setPackageInfo(packageConfig)
                    .setDataSource(dataSourceConfig)
                    .setGlobalConfig(gc)
                    .setStrategy(strategyConfig);
            //执行
            autoGenerator.execute();
        }
    

    #pojo

    //用户
    @Data
    @TableName("t_user")
    public class User implements Serializable {
        private static final long serialVersionUID=1L;
        @TableId(value = "u_id", type = IdType.AUTO)
        private Integer uId;
        private String name;
        private String password;
        @TableField(exist = false)
        private List<Role> roles;
    }
    //角色
    @Data
    @TableName("t_role")
    public class Role implements Serializable {
        private static final long serialVersionUID = 1L;
        @TableId(value = "r_id", type = IdType.AUTO)
        private Integer rId;
        private String role;
        @TableField(exist = false)
        private List<Perm> perms;
    }
    //权限
    @Data
    @TableName("t_perm")
    public class Perm implements Serializable {
        private static final long serialVersionUID = 1L;
        @TableId(value = "p_id", type = IdType.AUTO)
        private Integer pId;
        private String perm;
    }
    

    #mapper

    在主启动类上使用了@MapperScan, 无需再使用@Mapper

    //user
    public interface UserMapper extends BaseMapper<User> {
        @Select("SELECT * FROM t_user WHERE name =#{username}")
        @Results({
                @Result(property = "uId",column = "u_id",id = true),
                @Result(property = "roles",column = "u_id",
                        many = @Many(select = "com.chz.mapper.RoleMapper.queryRole",fetchType = FetchType.LAZY))
        })
        User queryUser(@Param("username") String username);
    }
    
    //role
    public interface RoleMapper extends BaseMapper<Role> {
        @Select("SELECT * FROM t_role WHERE r_id IN (SELECT r_id FROM t_u_r WHERE u_id = #{u_id})")
        @Results({
                @Result(property = "rId", column = "r_id"),
                @Result(property = "perms",column = "r_id",
                many =@Many(select = "com.chz.mapper.PermMapper.queryPerms",fetchType = FetchType.LAZY))
        })
        Role queryRole(@Param("u_id") Integer uid);
    }
    //perm
    public interface PermMapper extends BaseMapper<Perm> {
        @Select("SELECT * FROM t_perm WHERE p_id IN (SELECT p_id FROM t_r_p WHERE r_id = #{r_id})")
        List<Perm> queryPerms(@Param("r_id") Integer rId);
    }
    

    #Mvc配置类

    /**
     * Mvc配置类
     */
    @Configuration
    public class MvcConf implements WebMvcConfigurer {
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("index");
            registry.addViewController("/index").setViewName("index");
        }
    }
    

    #shiro配置类

    /**
     * shiro配置类
     * 三大核心 ShiroFilterFactoryBean: filter 过滤url,交给SecurityManager代理
     * SecurityManager: 代理filter和Realm
     * Realm: 连接数据库的桥梁,权限与用户认证
     */
    @Configuration
    public class ShiroConf {
        /**
         *
         * @param manager 安全代理
         */
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultSecurityManager manager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            //关联SecurityManager
            shiroFilterFactoryBean.setSecurityManager(manager);
            //通过set的方式设置url会放行请求
            //登入的url
            shiroFilterFactoryBean.setLoginUrl("/login");
            //登入成功的url(由于请求是通过ajax发送的,所以不会生效,这里只起请求放行作用)
            shiroFilterFactoryBean.setSuccessUrl("/index");
            //设置未授权跳转的url
            //shiroFilterFactoryBean.setUnauthorizedUrl("/");
            //设置过滤,必须使用LinkedHashMap,否则会出现资源加在一次就被拦截
            LinkedHashMap<String, String> filterChain = new LinkedHashMap<>();
        /*
        注意 anon 必须放在authc之上
        支持ant
        常用过滤器:
        1. anon (anonymous): 无需认证(登入)可以访问资源
        2. authc (authority): 必须认证才能访问
        3. user : 如果使用rememberMe的功能可以访问, 同时认证通过的也可以访问
        4. perms (permissions): 该资源必须拥有对应的资源权限才能访问
        5. roles: 该资源源必须拥有对应的角色才能访问
         */
            //filter会拦截所有请求,包括静态资源的请求
    //        filterChain.put("/css/**", "anon");
    //        filterChain.put("/js/**", "anon");
    //        filterChain.put("/img/**", "anon");
            filterChain.put("/assert/**","anon");
            //放行webjars
            filterChain.put("/webjars/**","anon");
            //放行druid
            filterChain.put("/druid/**", "anon");
            //登出放行
            filterChain.put("/logout", "anon");
            filterChain.put("/register","anon");
            filterChain.put("/", "anon");
            //除上述url外都必须认证通过才能访问, 为通过认证自动访问setLoginUrl()设置的url
            filterChain.put("/**", "authc");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChain);
            return shiroFilterFactoryBean;
        }
    
        /**
         * 关联Realm
         * @param userRealm
         * @return
         */
        @Bean
        public DefaultWebSecurityManager securityManager(Realm userRealm) {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            //关联realm
            securityManager.setRealm(userRealm);
            return securityManager;
        }
    
        /**
         * 用户认证与权限认证
         * @param matcher 指定Shiro加密方式
         */
        @Bean
        public Realm userRealm() {
            CustomizeRealm realm = new CustomizeRealm();
            //告诉Shiro使用MD5加密
            realm.setCredentialsMatcher(MD5());
            return realm;
        }
    
        /**
         *  MD5加密
         */
        private HashedCredentialsMatcher MD5() {
            HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
            //指定加密方式为MD5
            matcher.setHashAlgorithmName("MD5");
            //加密次数
            matcher.setHashIterations(5);
            matcher.setStoredCredentialsHexEncoded(true);
            return matcher;
        }
        /**
         * 开始shiro标签
         */
        @Bean
        public ShiroDialect dialect() {
            return new ShiroDialect();
        }
    

    #自定义Realm

    @Slf4j
    public class CustomizeRealm extends AuthorizingRealm {
        @Autowired
        private IUserService userService;
    
        /**
         * 获取用户权限和角色
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            return null;
        }
    
        /**
         * 登入认证
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
            //获取用户名
            String username = usernamePasswordToken.getUsername();
            User user = userService.queryUser(username);
            //判断数据库中是否有该用户
            if (ObjectUtils.isEmpty(user)) {
                //如果不存在抛出用户不存在异常
                throw new UnknownAccountException();
            }
            //获取加密后的密码
            String password = user.getPassword();
            //盐值
            ByteSource salt = ByteSource.Util.bytes(username);
            //密码校验由shiro完成
            //principal,hashedcredentials,salt,realName
            SimpleAuthenticationInfo info =
                    //校验加密后的密码盐值是否匹配
                    new SimpleAuthenticationInfo(username,
                            password,
                            salt,
                            getName());
            return info;
        }
    }
    

    #加密工具类

    /**
     * MD5加密工具类
     */
    public class MD5Utils {
        //加密方式可以时SHA1或是MD5
        private static final String ALGORITHM_NAME = "MD5";
        private static final int HASH_ITERATIONS = 5;
        private static final String SALT = "chz";
    
        //加密方式,加密对象,盐值,加密次数
        public static String encrypt(String password, String username) {
            SimpleHash hash = new SimpleHash(ALGORITHM_NAME, password, username, HASH_ITERATIONS);
            return hash.toHex();
        }
    
        public static String encrypt(String password) {
            SimpleHash hash = new SimpleHash(ALGORITHM_NAME, password, SALT, HASH_ITERATIONS);
            return hash.toHex();
        }
    }
    

    #controller

    @Slf4j
    @Controller
    public class UserController {
        @Autowired
        IUserService userService;
    
        @GetMapping("/login")
        public String toLogin() {
            return "login";
        }
        @ResponseBody
        @PostMapping("/register")
        public String register(@RequestBody User user){
            String password = MD5Utils.encrypt(user.getPassword(),user.getName());
            user.setPassword(password);
            userService.save(user);
            return "注册成功";
        }
        @ResponseBody
        @PostMapping(value = "/login")
        public String login(@RequestBody User user) {
            log.info("在执行用户认证时调用了数据库,原因不明");
            //获取当前用户
            Subject currentUser = SecurityUtils.getSubject();
            UsernamePasswordToken token =
                    new UsernamePasswordToken(user.getName(), user.getPassword());
            try {
                currentUser.login(token);
                //@ResponseBody返回的如果是一个字符串,直接写到客户端;如果是对象才会将对象转为json串
                //如果登入成功,返回0通过ajax判断重定向
                return "0";
                //为了安全不应该具体的显示错误
            } catch (UnknownAccountException e) {
                System.out.println("用户名错误");
                return "用户名错误或密码错误";
            } catch (IncorrectCredentialsException e) {
                System.out.println("密码错误");
                return "用户名错误或密码错误";
            }
        }
    
  • 相关阅读:
    subprocess模块讲解
    正则
    logging日志模块
    2-30hashlib模块讲解
    json pickle复习 shelve模块讲解
    XML、PyYAML和configparser模块讲解
    os模块
    2-25sys模块和shutil模块讲解
    随机生成模块
    时间模块
  • 原文地址:https://www.cnblogs.com/kikochz/p/12827752.html
Copyright © 2020-2023  润新知