springboot+shiro登录校验及权限验证

shiro所需要的包
<dependency> 
          <groupId>org.apache.shiro</groupId> 
          <artifactId>shiro-cas</artifactId> 
          <version>1.2.4</version> 
        </dependency> 
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-ehcache -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.4.0</version>
        </dependency>
public class AuthRealm extends AuthorizingRealm {
    
    @Autowired
    private ISysAdminService adminService;
    
    @Autowired
    private ISysAdminRoleService adminRoleService;
    
    @Autowired
    private ISysRolePermService rolePermService;
    
    //认证.登录
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("-----------------认证.登录-------------------");
        UsernamePasswordToken utoken = (UsernamePasswordToken) token;//获取用户输入的token
        String username = utoken.getUsername();
        //处理session
        SessionsSecurityManager securityManager = (SessionsSecurityManager) SecurityUtils.getSecurityManager();
        DefaultSessionManager sessionManager = (DefaultSessionManager) securityManager.getSessionManager();
      //获取当前已登录的用户session列表
        Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions();
        for (Session session : sessions) {
            //清除该用户以前登录时保存的session
//            IotdUserEntity en=(IotdUserEntity)(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY));
//            String phone=en.getPhone();
            //如果和当前session是同一个session,则不剔除
            if (SecurityUtils.getSubject().getSession().getId().equals(session.getId()))
                break;
            
            SysAdmin admin = (SysAdmin) (session.getAttribute("sysAdmin"));
            if (admin != null) {
                String adminName = admin.getLoginName();
                if (username.equals(adminName)) {
                    System.out.println(username + "已登录,剔除中...........");
                    sessionManager.getSessionDAO().delete(session);
                }
            }
        }
        //获取用户信息
        SysAdmin sysAdmin = adminService.getByLoginName(username);
        if(sysAdmin == null) {
            throw new UnknownAccountException();
        }
        if(sysAdmin.getIsLock() == 1) {//被锁定
            throw new LockedAccountException();
        }
        if(sysAdmin.getIsEnable() == 1) {//该账号未被启用
            throw new DisabledAccountException();
        }
        //放入shiro.调用CredentialsMatcher检验密码
        SimpleAuthenticationInfo rst = new SimpleAuthenticationInfo(sysAdmin, sysAdmin.getPassword(), this.getClass().getName());
        return rst;
    }

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        SysAdmin admin = (SysAdmin) principal.fromRealm(this.getClass().getName()).iterator().next();//获取session中的用户
        //获取该用户的所有角色
        List<Integer> listId = adminRoleService.getRoleIdByAdminId(admin.getSysAdminId());
        //查询该用户的权限
        List<String> list = rolePermService.getAuthCodeListByRoles(listId);
        info.addStringPermissions(list);
        return info;
    }


}
/**
 * shiro配置类
 * @author Administrator
 *
 */

@Configuration
public class ShiroConfiguration {
    
    /**
     * 创建ShiroFilterFactoryBean
     */
    @Bean(name="shiroFilter")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        
        /**
         * shiro常用拦截器
         *     anon: 无需认证就可访问
         *     authc: 需要认证才能访问
         *  user :使用rememberMe的功能可直接访问
         *  perms: 必须有资源的权限才可以访问
         *     role : 该资源必须得到角色的权限才可以访问
         */
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        
        return shiroFilterFactoryBean;
    }
    


    //配置核心安全事务管理器
    @Bean(name="securityManager")
    public DefaultWebSecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm) {
        System.err.println("--------------shiro已经加载----------------");
        DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
        manager.setRealm(authRealm);
        manager.setSessionManager(sessionManager());
        // <!-- 用户授权/认证信息Cache, 采用EhCache 缓存 -->
        manager.setCacheManager(ehCacheManager());
        //注入记住我管理器;
        manager.setRememberMeManager(rememberMeManager());
        return manager;
    }

    @Bean(name = "sessionManager")
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();
        return sessionManager;
    }
    
    //配置自定义的权限登录器
    @Bean(name="authRealm")
    public AuthRealm authRealm(@Qualifier("credentialsMatcher") CredentialsMatcher matcher) {
        AuthRealm authRealm=new AuthRealm();
        authRealm.setCredentialsMatcher(matcher);
        return authRealm;
    }
    
    //配置自定义的密码比较器
    @Bean(name="credentialsMatcher")
    public CredentialsMatcher credentialsMatcher() {
        return new CredentialsMatcher(ehCacheManager());
    }
    
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager manager) {
        AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(manager);
        return advisor;
    }

    //----------------------------------------------- 
    /**
     * LifecycleBeanPostProcessor,这是个DestructionAwareBeanPostProcessor的子类,
     * 负责org.apache.shiro.util.Initializable类型bean的生命周期的,初始化和销毁。
     * 主要是AuthorizingRealm类的子类,以及EhCacheManager类。
     *
     * @return
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * ShiroRealm,这是个自定义的认证类,继承自AuthorizingRealm, 负责用户的认证和权限的处理
     *
     * @return
     */
    @Bean(name = "myShiroRealm")
    @DependsOn("lifecycleBeanPostProcessor")
    public AuthRealm myShiroRealm() {
        AuthRealm myShiroRealm = new AuthRealm();
        return myShiroRealm;
    }

    /**
     * EhCacheManager,缓存管理,用户登陆成功后,把用户信息和权限信息缓存起来,
     * 然后每次用户请求时,放入用户的session中,如果不设置这个bean,每个请求都会查询一次数据库。
     *
     * @return
     */
    @Bean(name = "ehCacheManager")
    @DependsOn("lifecycleBeanPostProcessor")
    public EhCacheManager ehCacheManager() {
        EhCacheManager cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
        return cacheManager;
    }

    /**
     * 自定义shiroSession的cookie,如果不设置默认JSESSIONID,
     * 会与tomcat等默认cookie名重复,sessionIdCookie用于保存sessionId标识
     *
     * @return
     */
    @Bean(name = "rememberMeCookie")
    public SimpleCookie rememberMeCookie() {
        // 这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        // setcookie的httponly属性如果设为true的话,会增加对xss防护的安全系数。它有以下特点:
        // 设为true后,只能通过http访问,javascript无法访问
        // 防止xss读取cookie
        simpleCookie.setHttpOnly(true);
        // <!-- 记住我cookie生效时间30天 ,单位秒;-->
        simpleCookie.setMaxAge(259200);
        return simpleCookie;
    }

    /**
     * cookie管理对象;
     * 记住我的配置
     * @return
     */
    @Bean(name = "rememberMeManager")
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        return cookieRememberMeManager;
    }

    /**
     * DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。
     *
     * @return
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        daap.setProxyTargetClass(true);
        return daap;
    }

    /**
     * thymeleaf模板使用shiro注解
     * @return
     */
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }
    
}
/**
 * 自定义密码比较
 * @author Administrator
 * 
 *
 */
public class CredentialsMatcher extends SimpleCredentialsMatcher {
    
     @Autowired
     private ISysAdminService adminService;
    
     private Cache<String, AtomicInteger> passwordRetryCache;
     
     public CredentialsMatcher(CacheManager cacheManager) {
         passwordRetryCache = cacheManager.getCache("passwordRetryCache");
    }
    
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        
        // 获取登录用户的用户名
        String username = (String)token.getPrincipal();
        // 获取用户登录次数
        AtomicInteger retryCount = passwordRetryCache.get(username);
        if (retryCount == null) {
            // 如果用户没有登陆过,登陆次数加1 并放入缓存
            retryCount = new AtomicInteger(0);
            passwordRetryCache.put(username, retryCount);
        }
        if (retryCount.incrementAndGet() > 3) {
            // 如果用户登陆失败次数大于3次 抛出锁定用户异常  并修改数据库字段
            SysAdmin admin = adminService.getByLoginName(username);
            if (admin != null && admin.getIsLock() == 0){
                // 数据库字段 默认为 0  就是未锁定状态 所以 要改为1
                // 修改数据库的状态字段为锁定
                admin.setIsLock((byte) 1);
                adminService.updateById(admin);
            }
            // 抛出用户锁定异常
            throw new LockedAccountException();
        }
        // 判断用户账号和密码是否正确
        boolean matches = super.doCredentialsMatch(token, info);
        if (matches) {
            // 如果正确,从缓存中将用户登录计数 清除
            passwordRetryCache.remove(username);
        }
        return matches;
    }
    
    /**
     * 根据用户名 解锁用户
     * @param username
     * @return
     */
    public void unlockAccount(String username){
        passwordRetryCache.remove(username);
    }
}
/**
     * 生成验证码
     */
    @RequestMapping(value = "/getVerify")
    public void getVerify(HttpServletRequest request, HttpServletResponse response) {
        try {
            response.setContentType("image/jpeg");// 设置相应类型,告诉浏览器输出的内容为图片
            response.setHeader("Pragma", "No-cache");// 设置响应头信息,告诉浏览器不要缓存此内容
            response.setHeader("Cache-Control", "no-cache");
            response.setDateHeader("Expire", 0);
            RandomValidateCodeUtil randomValidateCode = new RandomValidateCodeUtil();
            randomValidateCode.getRandcode(request, response);// 输出验证码图片方法
        } catch (Exception e) {
            result(HTTP_VALID,"获取验证码失败",ICON_ERROR);
        }
    }
    
    /**
     * 登录校验
     * @param admin
     * @return
     * @throws Exception 
     */
    @RequestMapping(value="/checkLogin" ,method=RequestMethod.POST)
    @ResponseBody
//    @OperLog(operModul="登录",operType="checkLogin",operDesc="验证用户名密码")
    public AjaxResult checkLogin(HttpSession session ,SysAdmin admin,String captcha){
        try {
//            String random = (String) session.getAttribute("RANDOMVALIDATECODEKEY");
//            if(captcha.equals("") || !random.equals(captcha)) {
//                return result(HTTP_VALID,"验证码错误",ICON_EXIST);
//            }
            /**
             * shiro 认证操作
             */
            //1.获取Subject对象
            Subject subject = SecurityUtils.getSubject();
            //2.封装用户数量
            
            UsernamePasswordToken token = new UsernamePasswordToken(admin.getLoginName(), DesAesUtil.encryptDES(admin.getPassword()));
            //3.执行登录方法
        
            subject.login(token);
            SysAdmin loginAdmin = (SysAdmin)SecurityUtils.getSubject().getPrincipal();
            session.setAttribute("sysAdmin", loginAdmin);
            JSONObject json = new JSONObject();
            json.put("url", "/Bmc/sys-menu/menu");
            return result(HTTP_SUCCESS , MSG_SUCCESS , ICON_SUCCESS , json);
        }catch (UnknownAccountException e) {
            return result(HTTP_VALID , "用户名不存在" , ICON_EXIST);
        }catch(IncorrectCredentialsException e) {
            return result(HTTP_VALID , "密码不正确" , ICON_EXIST);
        }catch(NullPointerException e) {
            return result(HTTP_VALID , "验证码过期" , ICON_EXIST);
        }catch(LockedAccountException e) {
            return result(HTTP_VALID , "该账号由于登录次数过多已被锁定" , ICON_EXIST);
        }catch(DisabledAccountException e) {
            return result(HTTP_VALID , "该账号未被启用" , ICON_EXIST);
        }catch(Exception e) {
            return result(HTTP_VALID , "该账号存在异常" , ICON_EXIST);
        } 
    }

 

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <defaultCache
            eternal="false"
            maxElementsInMemory="1000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="0"
            timeToLiveSeconds="600"
            memoryStoreEvictionPolicy="LRU" />
            
 

    <!-- 登录失败次数缓存
        注意 timeToLiveSeconds 设置为300秒 也就是5分钟
         可以根据自己的需求更改
     -->
    <cache name="passwordRetryCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="300"
           overflowToDisk="false"
           statistics="true">
    </cache>
</ehcache>

 

posted @ 2022-06-17 13:44  陳丶  阅读(600)  评论(0编辑  收藏  举报