shiro分析以及应用过程

参考文章:

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

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

1.shiro的整个登录流程:

shiro分析以及应用过程_第1张图片

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

导入maven依赖

shiro分析以及应用过程_第2张图片

接下来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
     * 

* 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); // // // 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 customisedFilter = new HashMap<>(2); customisedFilter.put("orperms", orPermissionsAuthorizationFilter()); customisedFilter.put("corsAuthenticationFilter", corsAuthenticationFilter()); Map filterChainDefinitionMap = initUrl(); shiroFilterFactoryBean.setFilters(customisedFilter); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); } public CORSAuthenticationFilter corsAuthenticationFilter() { return new CORSAuthenticationFilter(); } /** * 初始化url路径 */ private Map initUrl() throws IOException { Map 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 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 map = new HashMap<>();
        map.put("code", 702);
        map.put("msg", "未授权");
        writer.write(JSON.toJSONString(map));
        writer.close();
        return false;
    }

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

 

 

你可能感兴趣的:(shiro)