SpringBoot入门九,添加shiro支持

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot入门九,添加shiro支持相关的知识,希望对你有一定的参考价值。

项目基本配置参考SpringBoot入门一,使用myEclipse新建一个SpringBoot项目,使用myEclipse新建一个SpringBoot项目即可。现在来给项目添加shiro支持,数据暂时硬编码,不连接数据库,具体内容如下:

1. pom.xml添加以下配置信息

<!-- 开启shiro依赖 -->
<dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring-boot-starter</artifactId>
        <version>1.4.0</version>
</dependency>

2. 创建shiro配置文件

2.1 自定义权限过滤器
package com.qfx.demo.shiro;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;

import com.qfx.demo.vo.SysUser;

/**
 * @功能描述:   拦截控制,用于替换shiro默认的roles拦截规则,改"并且(and)"为"或者 (or)"
 */
public class CustomRolesAuthorizationFilter extends AuthorizationFilter{
    private final Logger logger = LogManager.getLogger(getClass()); 
    /**
     * Overriding
     * @功能描述:设置同一个URL配置多个角色为"或者"的关系,默认为"并且",
     *                  如:/user/** = Role["admin,user"],默认必须满足"admin","user"条件,
     *                 改为"或者"之后只需要满足一个条件即可(Ini.Section中有此url,会走此方法)
     * @param request
     * @param response
     * @param obj
     * @return
     * @throws Exception
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request,ServletResponse response, Object obj) throws Exception {
        Subject subject = getSubject(request, response);
        // 验证是否登录
        if (null == subject.getPrincipals()) {
            return false;
        }
        // 获取请求地址
        HttpServletRequest hsq = (HttpServletRequest) request;
        String requestUrl = hsq.getServletPath();
        // 获取用户信息,这里返回的对象类型与登录验证时
        // new SimpleAuthenticationInfo(user, pwd, this.getName())中的第一个参数的类型需要保持一致
        SysUser user = (SysUser)subject.getPrincipals().getPrimaryPrincipal();

        System.out.println("--------1.开启用户["+user.getUserName()+"]访问["+requestUrl+"]的角色过滤--------");

        // 获取角色信息
        String[] rolesArray = (String[]) obj;
        if (rolesArray == null || rolesArray.length == 0) { //没有角色限制,有权限访问
            System.out.println("--------3.用户["+user.getUserName()+"]访问["+requestUrl+"]的角色过滤结束--------");
            logger.info("用户["+user.getUserName()+"]访问["+requestUrl+"]无角色限制,权限验证通过!");
                return true;
        }    
        for (int i = 0; i < rolesArray.length; i++) {    
                if (subject.hasRole(rolesArray[i])) { //若当前用户是rolesArray中的任何一个,则有权限访问  
                    System.out.println("--------3.用户["+user.getUserName()+"]访问["+requestUrl+"]的角色过滤结束--------");
                    logger.info("用户["+user.getUserName()+"]访问["+requestUrl+"]权限验证通过!");
                        return true;    
                }    
        }
        System.out.println("--------3.用户["+user.getUserName()+"]访问["+requestUrl+"]的角色过滤结束--------");
        logger.info("用户["+user.getUserName()+"]访问["+requestUrl+"]权限验证失败,禁止访问!");
        return false;
    }
}
2.2 自定义realm
package com.qfx.demo.shiro;

import java.util.HashSet;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import com.qfx.demo.cache.UserCache;
import com.qfx.demo.vo.SysUser;

public class UserRealm extends AuthorizingRealm {
    /**
     * Overriding
     * @功能描述:   权限验证
     * @param arg0
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 1.获取用户信息
        //   这里principals.getPrimaryPrincipal()的返回的对象类型与登录验证时
        //   new SimpleAuthenticationInfo(user, pwd, this.getName())中的第一个参数的类型需要保持一致
        SysUser user = (SysUser)principals.getPrimaryPrincipal();
        System.out.println("--------2.用户["+user.getUserName()+"]进行权限验证--------");
        // 2.单独定一个集合对象放置角色信息 
        Set<String> roles = new HashSet<String>();
        roles.add(user.getRoleName());
        // 3.查到权限数据,返回授权信息(要包括 上边的permissions)  
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);
        return simpleAuthorizationInfo;
    }

    /**
     * Overriding
     * @功能描述:   登陆验证
     * @param arg0
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        System.out.println("---------用户登录验证---------");
        // 1.获取登录名称
        String userName = token.getPrincipal().toString();
        // 2.根据登录名称获取用户信息(从缓存获取,正式项目从数据库)
        SysUser user = UserCache.getUserCacheMap(userName);
        if (null == user) {
            // 抛出账户不存在的异常
            throw new UnknownAccountException();
        }
        // 3.获取查询到到密码和盐值
        String pwd = user.getPassWord();
        String salt = user.getSalt();
        // 4.交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配
        //   new SimpleAuthenticationInfo(user, pwd, this.getName())中的user是SysUser对象,在其他接收的地方也要转成SysUser对象
        SimpleAuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user, pwd, this.getName());  
        // 设置盐值(salt = pwd+userName + salt)
        authcInfo.setCredentialsSalt(ByteSource.Util.bytes(pwd+userName + salt));
        return authcInfo; 
    }
}
2.3 自定义shiro核心配置类(取代xml配置文件)
package com.qfx.demo.shiro;

import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.qfx.demo.cache.MenuRoleCache;
import com.qfx.demo.vo.SysMenuRole;

@Configuration
public class ShiroConfig {
        /**
         * <h5>功能:凭证匹配器</h5>
         * @return 
         */
        @Bean
        public HashedCredentialsMatcher hashMatcher(){
                HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
                //采用md5加密,没有默认值.可以有MD5或者SHA-1,如果对密码安全有更高要求可以用SHA-256或者更高
                hashedCredentialsMatcher.setHashAlgorithmName("md5");
                //散列的次数,比如散列2次,相当于md5(md5(""));
                hashedCredentialsMatcher.setHashIterations(2);
                //默认是true,true时用的是Hex编码;false时用Base64编码
                hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
                return hashedCredentialsMatcher;
        }

        /**
         * <h5>功能:配置shiro session 的一个管理器</h5>
         * @return 
         */
        @Bean
        public DefaultWebSessionManager sessionManager(){
            DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
            defaultWebSessionManager.setSessionDAO(new MemorySessionDAO());
            // 隐藏在地址栏中显示的JSESSIONID
            defaultWebSessionManager.setSessionIdUrlRewritingEnabled(false);
            // session的失效时长,单位毫秒(这里设置为30分钟,实际项目请自行修改)
            defaultWebSessionManager.setGlobalSessionTimeout(1800000);
            // 间隔一定时间清理失效会话,单位毫秒(这里设置为每5分钟清理一次用户直接关闭浏览器造成的孤立会话,实际项目请自行修改)
            defaultWebSessionManager.setSessionValidationInterval(300000);
            // 描session线程,负责清理超时会话
            defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);
            return defaultWebSessionManager;
        }

    /**
     * <h5>功能:自定义realm认证类,继承自AuthorizingRealm,负责用户的认证和权限的处理</h5>
     * @param hashMatcher
     * @return 
     */
    @Bean
    public UserRealm userRealm(HashedCredentialsMatcher hashMatcher){
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(hashMatcher);
        return userRealm;
    }

    /**
         * <h5>功能:安全管理器</h5>
         * 权限管理,这个类组合了登陆,登出,权限,session的处理,是个比较重要的类
     * @param userRealm
     * @param sessionManager
     * @return 
     */
    @Bean
        public DefaultWebSecurityManager securityManager(UserRealm userRealm, DefaultWebSessionManager sessionManager){
                DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
                // 自定义realm
                securityManager.setRealm(userRealm);
                // shiro 会话管理
                securityManager.setSessionManager(sessionManager);
                return securityManager;
        }

        /**
         * <h5>功能:自定义权限过滤器</h5>
         * @param securityManager
         * @return 
         */
        @Bean
        public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            // 调用我们配置的安全管理器
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            // 配置我们的登录请求地址,非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
            shiroFilterFactoryBean.setLoginUrl("/view/error/401.jsp");
            // 设置无权限时跳转的URL
            shiroFilterFactoryBean.setUnauthorizedUrl("/view/error/403.jsp");

            Map<String, Filter> filter = shiroFilterFactoryBean.getFilters();
            filter.put("roles", new CustomRolesAuthorizationFilter());

            // 设置拦截器
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
            shiroFilterFactoryBean.setFilters(filter);

            // 对静态资源设置匿名访问,从resoutces/static后面开始写
            filterChainDefinitionMap.put("/css/**", "anon");
            // 可匿名访问的地址
            filterChainDefinitionMap.put("/", "anon");
            filterChainDefinitionMap.put("/index.jsp", "anon");
            filterChainDefinitionMap.put("/login/loginPage", "anon");
            filterChainDefinitionMap.put("/login/register", "anon");
            filterChainDefinitionMap.put("/login/login", "anon");
            // 请求 logout.do地址,shiro去清除session
            filterChainDefinitionMap.put("/logout", "logout");

            //循环url,逐个添加到section中。section就是filterChainDefinitionMap,
            //里面的键就是链接URL,值就是存在什么条件才能访问该链接
            Map<String, SysMenuRole> menuRoleMap = MenuRoleCache.menuRoleCacheMap;
            for (String key : menuRoleMap.keySet()) {
                filterChainDefinitionMap.put(key, "roles["+menuRoleMap.get(key).getRoleNames()+"]");
            }

            //所有url都必须认证通过才可以访问,必须放在最后
            filterChainDefinitionMap.put("/**", "authc");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
}

3.其他文件
javaBean文件和缓存类等信息就不从这里贴出来了,核心的就是上面的几个文件,想要应用的话,只要把对应的获取数据的地方修改成自己的实现就可以了;或者想要本示例源码的话,请点击这里进行下载。

以上是关于SpringBoot入门九,添加shiro支持的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot 入门:集成 Shiro 实现登陆认证和权限管理

SpringBoot 集成Shiro--官网

SpringBoot + Shiro (四)缓存&记住密码

springboot集成shiro实现权限缓存和记住我

springboot mybatis 后台框架平台 集成代码生成器 shiro 权限

Shiro安全框架——快速入门登录拦截用户认证请求授权