shiro分析以及应用过程

参考文章:

https://blog.csdn.net/jin5203344/article/details/53174341 

本篇文章我将会从两个方面去讲解,一个是从shiro的应用 第二个是我在项目中遇到的一些问题:

1.shiro的整个登录流程:

 2.首先我们来看看shiro的应用

导入maven依赖

接下来shiroconfig配置

@Configuration
@Slf4j
public class ShiroConfig {

    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * Shiro 权限相关注解 使用
     *
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。 注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在
     * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
     * <p>
     * Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过
     * 3、部分过滤器可指定参数,如perms,roles
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/rest/unauth");
        try {
            loadShiroFilterChain(shiroFilterFactoryBean);
        } catch (IOException e) {
            log.error("io读取异常");
            e.printStackTrace();
        }
        shiroFilterFactoryBean.setLoginUrl("/rest/login");
        return shiroFilterFactoryBean;
    }

    public OrPermissionsAuthorizationFilter orPermissionsAuthorizationFilter() {
        return new OrPermissionsAuthorizationFilter();
    }

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //设置自定realm
        securityManager.setRealm(getDatabaseRealm());
        //自定义缓存实现 使用redis
        securityManager.setCacheManager(cacheManager());
        //自定义sessoin管理,使用redis
        securityManager.setSessionManager(sessionManager());
        //注入记住我管理器
//        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }

    /**
     * 配置shiro redisManager 使用的是shiro-redis开源插件
     */
    @ConfigurationProperties(prefix = "redis.shiro")
    @Bean
    public RedisManager redisManager() {
        return new RedisManager();
    }

    /**
     * cacheManager 缓存 redis实现 使用的是shiro-redis开源插件
     */
    @Bean
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    //自定义sessionManager
    @Bean
    public SessionManager sessionManager() {
        MySessionManager mySessionManager = new MySessionManager();
        mySessionManager.setSessionDAO(redisSessionDAO());
//        mySessionManager.setSessionIdCookie(simpleCookie());
        return mySessionManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis 使用的是shiro-redis开源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }


    /**
     * cookie对象;
     */
//    @Bean
//    public SimpleCookie simpleCookie() {
//        //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
//        SimpleCookie simpleCookie = new SimpleCookie(
//            CookieRememberMeManager.DEFAULT_REMEMBER_ME_COOKIE_NAME);
//        //setcookie的httponly属性如果设为true的话,会增加对xss防护的安全系数。它有以下特点:
//        //setcookie()的第七个参数
//        //设为true后,只能通过http访问,javascript无法访问
//        //防止xss读取cookie
//        // 是否只在https情况下传输
//        simpleCookie.setSecure(false);
//        //<!-- 记住我cookie生效时间30天 ,单位秒;-->
//        simpleCookie.setMaxAge(2592000);
//        return simpleCookie;
//    }

    /**
     * cookie管理对象;记住我功能
     */
//    @Bean
//    public CookieRememberMeManager rememberMeManager() {
//        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
//        cookieRememberMeManager.setCookie(simpleCookie());
//        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
//        cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
//        return cookieRememberMeManager;
//    }

    /**
     * 凭证匹配器 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 所以我们需要修改下doGetAuthenticationInfo中的代码;
     * )
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(1024);//散列的次数,比如散列两次,相当于 md5(md5(""));
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return hashedCredentialsMatcher;
    }

    @Bean
    public ShiroRealm getDatabaseRealm() {
        ShiroRealm myShiroRealm = new ShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    /**
     * 开启shiro aop注解支持. 使用代理方式;所以需要开启代码支持;
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 加载shiroFilter权限控制规则(然后从数据库读取配置)
     */
    private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean) throws IOException {
        //自定义拦截器
        Map<String, Filter> customisedFilter = new HashMap<>(2);
        customisedFilter.put("orperms", orPermissionsAuthorizationFilter());
        customisedFilter.put("corsAuthenticationFilter", corsAuthenticationFilter());
        Map<String, String> filterChainDefinitionMap = initUrl();
        shiroFilterFactoryBean.setFilters(customisedFilter);
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    }

    public CORSAuthenticationFilter corsAuthenticationFilter() {
        return new CORSAuthenticationFilter();
    }

    /**
     * 初始化url路径
     */
    private Map<String, String> initUrl() throws IOException {
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("url.properties");
        InputStreamReader inputStreamReader = new InputStreamReader(in);
        BufferedReader br = new BufferedReader(inputStreamReader);
        String str = null;
        try {
            while ((str = br.readLine()) != null) {
                if (StringUtils.isEmpty(str) || str.contains("#")) {
                    continue;
                }
                String[] arr = str.split("=");
                filterChainDefinitionMap.put(arr[0].trim(), arr[1].trim());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                br.close();
            }
        }
        return filterChainDefinitionMap;
    }
initUrl()方法需要解释一下,在filterChainDefinitionMap中需要配置一系列的拦截路径(太长了),因此我将拦截路径配置到了配置文件中,同样我也推荐大家这样去做,这样管理起来也是非常方便.

下面就是开始

ShiroRealm.java
@Component
public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private LoginInfoService loginInfoService;

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
            throws AuthenticationException {
        System.out.println("欢迎认证");
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //TODO token.setRememberMe(true)
        String username = token.getPrincipal().toString();
        LoginInfo loginInfo = loginInfoService.getByUserName(username);
        if (loginInfo == null) {
            return null;
        } else {
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, loginInfo.getPassword(),
                    this.getClass().getSimpleName());
            clearCachedAuthorizationInfo(token.getPrincipal());
            return info;
        }

    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("开始授权!");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 能进入到这里,表示账号已经通过验证了
        String userName = (String) principalCollection.getPrimaryPrincipal();
        LoginInfo loginInfo = loginInfoService.getByUserName(userName);
        String type = loginInfo.getType();
        Set<String> permissions = new HashSet<>();
        switch (type) {
            case "1":
                type = ApiConstant.HQ_FINANCE + ":" + "1";
                break;
            case "2":
                type = ApiConstant.SUB_COMPANY_MANAGER + ":" + "2";
                break;
            case "3":
                type = ApiConstant.SUB_COMPANY_FINANCE + ":" + "3";
                break;
            default:
                break;
        }
        permissions.add(type);
        //给用户添加type 代表具有该类型的权限 加入如类型为admin
        info.setStringPermissions(permissions);
        return info;

这样就所有的shiro就可以使用了,这里还有一个地方我需要解释,由于shiro的过滤器都是且的关系,一旦权限或者角色出现或的关系时无法解决,那这个时候shiro自带的过滤器就无法满足我们的需求了  这个时候就需要去重写我们的过滤器.

例如这里我重写了权限过滤器.

public class OrPermissionsAuthorizationFilter extends AuthorizationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
        if (((HttpServletRequest) request).getMethod().toUpperCase().equals("OPTIONS")) {
            return true;
        }
        Subject subject = getSubject(request, response);
        String[] perms = (String[]) mappedValue;

        for (String perm : perms) {
            String[] permArr = perm.split(":");
            if (!isAccessAllowedRealization(permArr, subject, request, response)) {
                return false;
            }
        }

        return true;
    }

    /**
     * 权限过滤实现
     *
     * @return
     */
    private boolean isAccessAllowedRealization(String[] permArr, Subject subject, ServletRequest request, ServletResponse response) throws IOException {

        if (permArr.length > 1) {
            // 权限校验不通过返回的url链接
            String unauthorizedUrl = getUnauthorizedUrl();

            String username = null;
            try {
                username = (String) subject.getPrincipals().getPrimaryPrincipal();
            } catch (NullPointerException e) {
                log.error("用户试图不进行登录进入系统!");
                e.printStackTrace();
                return false;
            }
            LoginInfoService loginInfoService = SpringContextUtils.getContext().getBean(LoginInfoService.class);
            LoginInfo user = loginInfoService.getByUserName(username);
            if (!isContains(permArr, request, response, unauthorizedUrl, user)) return false;
        }
        return true;
    }

    private boolean isContains(String[] permArr, ServletRequest request, ServletResponse response, String unauthorizedUrl, LoginInfo user) throws IOException {
        if (!Arrays.asList(permArr).contains(user.getType())) {
            return false;
        }
        return true;
    }

    /**
     * 会话超时或权限校验未通过的 因为在校验权限的时候已经进行redirect处理所以这边暂时不处理
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        HttpServletResponse res = (HttpServletResponse) response;
        res.setHeader("Access-Control-Allow-Origin", "*");
        res.setStatus(HttpServletResponse.SC_OK);
        res.setHeader("content-type", "text/html;charset=UTF-8");
        res.setCharacterEncoding("UTF-8");
        PrintWriter writer = res.getWriter();
        Map<String, Object> map = new HashMap<>();
        map.put("code", 702);
        map.put("msg", "未授权");
        writer.write(JSON.toJSONString(map));
        writer.close();
        return false;
    }

那么角色过滤器呢  同样也是可以的 !

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值