shiro的基本使用

187 阅读4分钟

1.shiro配置

        <!--shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-web-starter</artifactId>
            <version>1.8.0</version>
        </dependency>
        <!--shiro redis缓存权限-->
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis-spring-boot-starter</artifactId>
            <version>3.3.1</version>
        </dependency>

2.使用步骤(基本配置步骤)

  1. shiroConfig文件中设置域

     @Bean
        public DefaultWebSecurityManager securityManager() {
            DefaultWebSecurityManager dws = new DefaultWebSecurityManager();
            // 设置域 (认证、授权 需要调用dao类 这个类中完成的登陆、查询、curd)
            dws.setRealm(myRealm());
            return dws;
        }
    
  2. 在设置域的同时创建myRealm配置文件,继承AuthorizingRealm类

    
    public class  MysqlRealm extends AuthorizingRealm {
    
        @Autowired
        SysUserMapper sysUserMapper;
    
    /**
         * 授权方法 当需要验证当前用户是否具有角色或授权时, 会自动调用
         **/
    
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection p) {
            return null;
        }
    
    /**
         * 认证方法 当用户登录时,会自动调用
         *
         * 登录流程:
         * 1. 用户输入账号密码 保存在usernamePasswordTken对象中
         * 2. 登录时会自动调用认证方法, 拿到登录的用户名去数据库查询
         * 3. 如果用户名不存在 抛出相应异常,表示登录失败
         * 4. 如果数据库存在该用户 就进行密码对比, 对比失败 抛出密码不对的异常
         * 5. 如果没有任何异常抛出 说明比对成功, 登录成功
         * */
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken a) throws AuthenticationException {
            // 拿到登录时 输入的用户名
            String username = (String) a.getPrincipal();
            // 去数据库查询该用户是否存在
            SysUser user = sysUserMapper.findUser(username);
            // 如果账号不存在 抛出异常
            if(user == null) {
                throw  new UnknownAccountException();
            }
            // 判断账号是否被锁定 密码不需要自己抛异常
            if(user.getSuo()==1) {
                throw new LockedAccountException();
            }
            // 设置得到数据库的盐
            ByteSource bytes = ByteSource.Util.bytes(user.getSalt());
            // 将数据库的账号密码对象 返回出去 和登录输入的账号密码进行对比
            SimpleAuthenticationInfo sai = new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),bytes,getName());
    
            return sai;
        }
    }
    
  3. 在Controller中进行简单使用

        @Autowired
        SysUserMapper sysUserMapper;
       
        @RequestMapping("login")
                public String login(String yhm, String pwd, RedirectAttributes ra, HttpSession session,String jzw){
       
                // 拿到当前操作对象
                Subject subject = SecurityUtils.getSubject();
       
                UsernamePasswordToken token =  new UsernamePasswordToken(yhm,pwd);
                try {
                    if(jzw!=null){ // 勾选记住我
                        token.setRememberMe(true);
                    }
                    subject.login(token);
                    //登录成功后保存业务到session
                    SysUser user = sysUserMapper.findUser(yhm);
                    session.setAttribute("users",user);
                    return "index";
                } catch (UnknownAccountException uae){
                    ra.addFlashAttribute("msg","用户名不存在");
                }
                catch (IncorrectCredentialsException ice){
                    ra.addFlashAttribute("msg","密码错误");
       
                }
                catch (LockedAccountException lae){
                    ra.addFlashAttribute("msg","用户已被锁定");
                }
                // 登录失败,重定向到登录页
                return "redirect:/";
        }
    

3.shiro最终配置

1.ShiroConfig,主要配置
  1. ShiroDialect :thymleaf中使用shiro标签库
  2. DefaultWebSecurityManager:设置域,设置会话管理器、记住我管理器
  3. Realm:加密设置,缓存配置(重点,提交登录时账号密码比对效率)
  4. ShiroFilterFactoryBean: shiro过滤器(重点)。
  5. CookieRememberMeManager:自定义cookie参数
package com.zking.config;

@Configuration
public class ShiroConfig {
    // thymleaf标签中使用shiro标签库
    @Bean
    public ShiroDialect sd() {
        return new ShiroDialect();
    }

    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager dws = new DefaultWebSecurityManager();

        // 设置会话管理器
        dws.setSessionManager(new DefaultWebSessionManager());
        // 设置记住我管理器
        dws.setRememberMeManager(ck());
        // 设置域 (认证、授权 需要调用dao类 这个类中完成的登陆、查询、curd)
        dws.setRealm(myRealm());
        return dws;
    }

    @Bean
    public CookieRememberMeManager ck() {
        CookieRememberMeManager ck = new CookieRememberMeManager();
        SimpleCookie sc = new SimpleCookie();
        sc.setHttpOnly(true); //防止js读取cookie
        sc.setMaxAge(40); // 设置cookir时长
        sc.setName("zhg"); // cookie名字
        ck.setCookie(sc);
        return ck;
    }

    @Bean
    public Realm myRealm() {
        MysqlRealm realm = new MysqlRealm();
        // 设置密码加密,登陆时进行加密对比
        HashedCredentialsMatcher hcm = new HashedCredentialsMatcher();
        hcm.setHashAlgorithmName("sha-256");
        hcm.setHashIterations(100);
        realm.setCredentialsMatcher(hcm);
        /*// 配置redis缓存
        RedisCacheManager cm = new RedisCacheManager();
        realm.setCacheManager(cm);*/
        return realm;
    }

    // shiro过滤器对象
    @Bean("shiroFilterFactoryBean") // 在spring容器中的id 必须是这个
    public ShiroFilterFactoryBean filterFactoryBean() {
        ShiroFilterFactoryBean sff = new ShiroFilterFactoryBean();
        // 设置安全管理器对象
        sff.setSecurityManager(securityManager());

        ///基本设置///
        // 检测到没有登录的跳转地址
        sff.setLoginUrl("/login.html");
        // 检测到没有权限的跳转地址
        sff.setUnauthorizedUrl("/qx.html");

        // 自定义过滤器,没有先后顺序
        Map<String , Filter> myFilter = new HashMap();
        myFilter.put("myroles",new MyRolesFilter());
        // 注销过滤器
        LogoutFilter logout = new LogoutFilter();
        logout.setRedirectUrl("/index.html");
        myFilter.put("logout",logout);
        sff.setFilters(myFilter);

        // 控制器过滤设置,有先后顺序 //
        /*
        * anon 允许匿名访问,不需要登录
        * user 需要登录后才能进行访问
        * roles 需要具有指定的所有角色
        * perms 需要具有指定的所有权限
        * logout 注销
        * authc 一定要输入账号密码,不包含记住我,安全性更高
        * */
        Map<String ,String > map = new LinkedHashMap<>();
        map.put("/*.html","anon");
        map.put("/zhuxiao","logout"); // 注销登录  LogoutFilter类 底层原理
/*        map.put("/songs/find","anon");
        map.put("/songs/delete","user,roles[经理,组长]");
        map.put("/songs/update","user");
        map.put("/songs/insert","authc");*/
//        map.put("songs/","");
        sff.setFilterChainDefinitionMap(map);
        return sff;
    }
}

2.MyRealm中主要配置

  1. 继承AuthorizingRealm
  2. 实现授权方法:doGetAuthorizationInfo
  3. 实现登录方法:doGetAuthenticationInfo
package com.zking.config;

public class  MysqlRealm extends AuthorizingRealm {

    @Autowired
    SysUserMapper sysUserMapper;

/**
     * 授权方法 当需要验证当前用户是否具有角色或授权时, 会自动调用
     **/

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection p) {
        // 得到当前登录的用户名
        String yhm = (String) p.getPrimaryPrincipal();
        // 查询该用户的角色与权限
        List<String> roles = sysUserMapper.findRoles(yhm);
        List<String> perms = sysUserMapper.findPerms(yhm);

        System.out.println(yhm+"具有角色"+roles);
        System.out.println(yhm+"具有权限"+perms);

        // 授权并返回
        SimpleAuthorizationInfo s=  new SimpleAuthorizationInfo();
        s.addRoles(roles);
        s.addStringPermissions(perms);
        return s;
    }

/**
     * 认证方法 当用户登录时,会自动调用
     *
     * 登录流程:
     * 1. 用户输入账号密码 保存在usernamePasswordTken对象中
     * 2. 登录时会自动调用认证方法, 拿到登录的用户名去数据库查询
     * 3. 如果用户名不存在 抛出相应异常,表示登录失败
     * 4. 如果数据库存在该用户 就进行密码对比, 对比失败 抛出密码不对的异常
     * 5. 如果没有任何异常抛出 说明比对成功, 登录成功
     * */

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken a) throws AuthenticationException {
        // 拿到登录时 输入的用户名
        String username = (String) a.getPrincipal();
        // 去数据库查询该用户是否存在
        SysUser user = sysUserMapper.findUser(username);
        // 如果账号不存在 抛出异常
        if(user == null) {
            throw  new UnknownAccountException();
        }
        // 判断账号是否被锁定 密码不需要自己抛异常
        if(user.getSuo()==1) {
            throw new LockedAccountException();
        }
        // 设置得到数据库的盐
        ByteSource bytes = ByteSource.Util.bytes(user.getSalt());
        // 将数据库的账号密码对象 返回出去 和登录输入的账号密码进行对比
        SimpleAuthenticationInfo sai = new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),bytes,getName());

        return sai;
    }
}