Springboot 2.13 整合Shiro安全框架(仅供参考)

1、 依赖(更新时间: 2019/11/05 14:50

<!-- shiro-spring权限管理 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.4.1</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.4.1</version>
</dependency>
<!-- shiro 缓存 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>1.4.1</version>
</dependency>
<!-- shiro控制按钮显示 -->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>

 

2、缓存文件(创建在resources下面就行,内容如下): ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="缓存自己看着来">

    <!-- 磁盘缓存位置 -->
    <diskStore path="java.io.tmpdir"/>

    <!-- 默认缓存 -->
    <defaultCache
            maxEntriesLocalHeap="1000"
            eternal="false"
            timeToIdleSeconds="3600"
            timeToLiveSeconds="3600"
            overflowToDisk="false">
    </defaultCache>

    <!-- 登录记录缓存 锁定10分钟 -->
    <cache name="loginRecordCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>

</ehcache>
	

 3、自定义Realm

import com.cocosum.blog.commons.utils.SessionUtils;
import com.cocosum.blog.system.entity.Permissions;
import com.cocosum.blog.system.entity.Role;
import com.cocosum.blog.system.entity.RolePermissions;
import com.cocosum.blog.system.entity.UserInfo;
import com.cocosum.blog.system.service.IPermissionsService;
import com.cocosum.blog.system.service.IRolePermissionsService;
import com.cocosum.blog.system.service.IRoleService;
import com.cocosum.blog.system.service.IUserInfoService;
import org.apache.shiro.authc.*;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;


/**
 * ShiroRealm类:验证,以及权限的添加
 * @author: fanyuke
 * @date 2019年11月05日下午12:05:52
 */
public class SysShiroRealm extends AuthorizingRealm {
	
	private static final Logger log = LoggerFactory.getLogger(AuthorizingRealm.class);

    @Lazy  // 懒加载,因为我个人用aop来记录日志
    @Autowired  // 用户
    private IUserInfoService userInfoService;
    
    @Lazy
    @Autowired // 角色
    private IRoleService roleService;
    
    @Lazy
    @Autowired // 角色与权限关系
    private IRolePermissionsService rolePermissionsService;
    
    @Lazy
    @Autowired // 权限
    private IPermissionsService permissionsService;
    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    	// principals  用户验证的凭证信息 
//        log.info("授权");
        UserInfo user = (UserInfo) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = null;
        try {
            authorizationInfo = new SimpleAuthorizationInfo();
            // 查询用户
            UserInfo userInfo = userInfoService.getUserInfoByUserName(user.getUserName());
            // 查询角色
            Role role = roleService.getRoleByRoleId(userInfo.getUserRoleId());
            authorizationInfo.addRole(role.getRoleCode());	// 角色标识符
//            log.info("授权角色: " + role.getRoleName() + "   角色标识符: " + role.getRoleCode());

            // 查询出权限标识符  用户list接受
            List<String> listPermissions = new ArrayList<>();
            
            // 如果说是超级管理员  添加所有的权限
            if (SessionUtils.isAdmin()) {
            	// 查询出所有的权限
            	List<Permissions> list = permissionsService.listPermissionsAll();
            	for (int i = 0; i < list.size(); i++) {
            		// 防止爆空
            		if (list.size() == 0) {
                		continue;
                	}
            		if ("#".equals(list.get(i).getPermissionsCode())) {
            			continue;
            		}
//            		log.info("正在授权:" + list.get(i).getPermissionsCode());
            		// 添加所有权限
        			listPermissions.add(list.get(i).getPermissionsCode());
				}
            	 // 添加权限
                authorizationInfo.addStringPermissions(listPermissions);
            } else { // 如果不是超级管理员的情况下查询出相应的权限
            	// 查询出角色对应的权限id
                List<RolePermissions> listRolePermissions = rolePermissionsService
                		.queryRolePermissionsByRoleId(role.getRoleId());
    			
                for (int i = 0; i < listRolePermissions.size(); i++) {
                	// 防止爆空
                	if (Objects.isNull(listRolePermissions)) {
                		continue;
                    }
                	Permissions permissions = permissionsService.
                			getPermissionsById(listRolePermissions.get(i).getPermissionsId());
                	if ("#".equals(permissions.getPermissionsCode())) {
                		continue;
                	}
                	listPermissions.add(permissions.getPermissionsCode());
                	// 添加权限标识符
//                	log.info("正在授权:" + permissions.getPermissionsCode());
    			}
                // 添加权限
                authorizationInfo.addStringPermissions(listPermissions);
            }
            
        } catch (UnknownAccountException e) {
            log.error("授权错误{}", e.getMessage());
//            e.printStackTrace();
        }
        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        log.info("登录认证");
        String userName = (String) token.getPrincipal();
        UserInfo userInfo = userInfoService.getUserInfoByUserName(userName);
        if(userInfo == null) {
            throw new UnknownAccountException(); // 没找到帐号
        }

        /*if(Boolean.TRUE.equals(user.getLocked())) {
            throw new LockedAccountException(); //帐号锁定
        }*/

        //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以在此判断或自定义实现
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
        		userInfo, // 用户对象
        		userInfo.getUserPassword(), //密码
                ByteSource.Util.bytes(userInfo.getSaltCode()), // 盐 (salt)
                getName()  //realm name
        );

        return authenticationInfo;
    }

}

 4、Shiro的配置文件

 

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
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.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

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

/**
 * shiro配置文件
 * @author : fyk
 * @create : 2019/11/05 9:34
 **/
@Configuration
public class ShiroConfig {

    /**
     * 注入自定义的realm
     * 告诉shiro如何获取用户信息来做登录认证和授权
     * 将自己的验证方式加入容器,否则是不生效的
     * @author : fyk
     * @create : 2019/11/05 9:40
     **/
    @Bean
    public Realm realm() {
        SysShiroRealm sysRealm = new SysShiroRealm();
        // 根据上面的主方法得到加密后的密码放到数据库中
        sysRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return sysRealm;
    }

    /**
     * 权限管理,配置主要是Realm的管理认证
     * securityManager是shiro的核心
     * @author : fyk
     * @create : 2019/11/05 9:33
     **/
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 注入自定义的realm;
        securityManager.setRealm(realm());
        // 注入缓存管理器;
        securityManager.setCacheManager(ehCacheManager());
        // 注入session的管理
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /**
     * shiro session的管理(会话管理)
     * @author : fyk
     * @create : 2019/11/05 14:30
     **/
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // 去掉shiro登录时url里的JSESSIONID
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }

    /**
     * shiro缓存管理器,需要注入到安全管理器:
     * securityManager可见securityManager是整个shiro的核心;
     * @author : fyk
     * @create : 2019/11/4 9:40
     **/
    @Bean
    public EhCacheManager ehCacheManager() {
        EhCacheManager cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
        return cacheManager;
    }

    /**
     * 这里统一做鉴权,即判断哪些请求路径需要用户登录,哪些请求路径不需要用户登录。
     * 这里只做鉴权,不做权限控制,因为权限用注解来做。
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(org.apache.shiro.mgt.SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 跳转到登录页面
        shiroFilterFactoryBean.setLoginUrl("/login");
//		shiroFilterFactoryBean.setSuccessUrl("/index");
        // 需要登录拦截的访问
        filterChainDefinitionMap.put("/js/**.js", "authc");        // 模块的js
        filterChainDefinitionMap.put("/**.html", "authc");         // 页面

        // 不需要拦截的访问
        filterChainDefinitionMap.put("/login", "anon");            // 登录
        filterChainDefinitionMap.put("/logout", "logout");         // 退出系统的过滤器
        filterChainDefinitionMap.put("/validatorLogin", "anon");   // 验证登录
        filterChainDefinitionMap.put("/errorPage/**", "anon");     // 错误页面
        filterChainDefinitionMap.put("/static/**", "anon");        // 今天资源

        // 排除以上的拦截之后,拦截所有
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }


    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),
     * 需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /***
     * 开启shiro aop注解支持,使用代理方式;所以需要开启代码支持; 加入注解的使用,不加入这个注解不生效
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

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

    /**
     * 没有权限跳转页面 异常处理 也可以通过实现HandlerExceptionResolver接口方式
     * @author : fyk
     * @create : 2019/11/4 9:49
     **/
    @Bean
    public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
        Properties properties = new Properties();
        // 没有权限的时候捕获异常 跳转到没有权限的界面
        properties.setProperty("AuthorizationException", "/common/errorpermissions");
        simpleMappingExceptionResolver.setExceptionMappings(properties);
        return simpleMappingExceptionResolver;
    }

    /**
     * 控制按钮显示 <a shiro:hasPermission="system:systemLog:delete"><a/>
     * @author: fanyuke
     * @Date : 2018年8月13日 上午10:33:12
     */
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

}
  • 注意用了Shiro之后不要用实现 implements WebMvcConfigurer接口了,不要给自己搞两个拦截器。
    public void addInterceptors(InterceptorRegistry registry) {}
    
  • 不需要实现拦截器接口了,当然你如果有多个资源路径可以实现WebMvcConfigurer,但是只需要重写
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
       registry.addResourceHandler("/js/**").addResourceLocations("classpath:/js/");
       registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
    }
    不要重写addInterceptors接口。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cocosum

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值