在SpringBoot中使用Shiro安全框架配置方案:
1,首先引入Shiro的Maven jar包:
<!-- 权限框架 begin-->
<dependency><!-- 核心 -->
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency><!-- 集成Spring -->
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency><!-- 缓存机制 -->
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.0</version>
</dependency>
<!-- 权限框架 end-->
2,引入Shiro缓存配置:
<!-- 在resource下新建 ehcache-shiro.xml -->
<ehcache updateCheck="false" name="shiroCache">
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"/>
</ehcache>
3,进行Shiro配置:
(1),自定义授权认证:
package com.gy.demo.config.shiro;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.gy.demo.entity.Power;
import com.gy.demo.entity.User;
import com.gy.demo.entity.UserLogin;
import com.gy.demo.service.RolePowerService;
import com.gy.demo.service.UserLoginService;
import com.gy.demo.service.UserService;
import jdk.nashorn.internal.parser.Token;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Resource;
import java.util.*;
/**
* Description : 自定义的 Shiro 认证授权
*
* 域,Shiro从Realm获取安全数据(如用户、角色、权限),
* 就是说SecurityManager要验证用户身份,
* 那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;
* 也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;
* 可以把Realm看成DataSource,即安全数据源;
*
* Created by com on 2017/12/14
*
* @author geYang
**/
public class AuthorRealm extends AuthorizingRealm {
private Logger logger = LoggerFactory.getLogger(AuthorRealm.class);
@Resource
private UserService userService;
@Resource
private UserLoginService userLoginService;
@Resource
private RolePowerService rolePowerService;
/**
* 认证(登录时调用) 获取身份验证信息
* */
@Override
protected AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken authenticationToken) throws AuthenticationException {
logger.info("Shiro: 进行身份验证");
String accessToken = (String) authenticationToken.getPrincipal();
//根据accessToken,查询用户信息
User user = userService.selectOne(new EntityWrapper<User>().eq("phone", accessToken));
if(user==null){
return null;
}
//获取用户登录密码
UserLogin userLogin = userLoginService.selectById(user.getId());
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user,userLogin.getPassword(),
ByteSource.Util.bytes(user.getPhone()),"AuthorRealm");
return simpleAuthenticationInfo;
}
/**
* 授权(验证权限时调用) 根据用户身份获取授权信息
* */
@Override
protected AuthorizationInfo doGetAuthorizationInfo (PrincipalCollection principalCollection) {
logger.info("Shiro: 进行权限验证");
//获取用户信息
User user = principalCollection.oneByType(User.class);
//获取到用户权限列表
List<Power> rolePowerList = rolePowerService.getRolePower(user.getRoleId());
Set<String> powerCodeSet = new HashSet<>();
Iterator<Power> it = rolePowerList.iterator();
while (it.hasNext()){
powerCodeSet.add(it.next().getCode());
}
List<String> powerCodeList = new ArrayList<>();
powerCodeList.addAll(powerCodeSet);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addStringPermissions(powerCodeList);
return simpleAuthorizationInfo;
}
}
(2),注入配置
package com.gy.demo.config.shiro;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 java.util.LinkedHashMap;
import java.util.Map;
/**
* Description : Shiro 权限框架配置
*
* ---------------------------------------------------------
* 1、应用代码通过Subject来进行认证和授权,
* 而Subject又委托给SecurityManager;
*
* 2、我们需要给Shiro的SecurityManager注入Realm,
* 从而让SecurityManager能得到合法的用户及其权限进行判断。
* ---------------------------------------------------------
* @since 2017/12/14
* @author geYang
**/
@Configuration
public class ShiroConfig {
private Logger logger = LoggerFactory.getLogger(ShiroConfig.class);
/**
* TODO 过滤文件
* @param securityManager 安全机制
* @return ShiroFilterFactoryBean
* @since 2017/12/18 10:07
* @author geYang
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
logger.info("Shiro: 开启拦截验证");
ShiroFilterFactoryBean shirFilter = new ShiroFilterFactoryBean();
// SecurityManager
shirFilter.setSecurityManager(securityManager);
// 登陆页面
shirFilter.setLoginUrl("/login");
// 登录成功后要跳转的链接
shirFilter.setSuccessUrl("/index");
// 未授权界面
shirFilter.setUnauthorizedUrl("/500.html");
// 拦截器
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
// 配置退出过滤器,其中的具体的退出代码 Shiro 已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
// 过滤链
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/druid/**", "anon");
// 登录页面
filterChainDefinitionMap.put("/login", "anon");
// 登录提交数据
filterChainDefinitionMap.put("/admin/adminLogin", "anon");
// 注册界面
filterChainDefinitionMap.put("/user/register.html", "anon");
// 注册提交数据
filterChainDefinitionMap.put("/admin/register", "anon");
// 发送邮箱验证码
filterChainDefinitionMap.put("/admin/sencCode", "anon");
// 判断用户名是否存在
filterChainDefinitionMap.put("/admin/isUsername/**", "anon");
// 判断邮箱是否存在
filterChainDefinitionMap.put("/admin/isEmail/**", "anon");
// 公共数据
filterChainDefinitionMap.put("/public/**", "anon");
// 其他
filterChainDefinitionMap.put("/**", "authc");
/* *
* anon:所有url都都可以匿名访问;
* authc: 需要认证才能进行访问;
* user:配置记住我或认证通过可以访问;
* */
shirFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shirFilter;
}
/**
* TODO 核心安全管理器;即所有与安全有关的操作都会与SecurityManager交互;
*------------------------------------------------------------------------
* SecurityManager 相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;
* 是Shiro的心脏;所有具体的交互都通过 SecurityManager 进行控制;
* 它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。
*-------------------------------------------------------------------------
* @return SecurityManager
* @since
* @author geYang
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm());
securityManager.setCacheManager(ehCacheManager());
return securityManager;
}
/**
* 身份认证realm
* */
@Bean
public AuthorRealm myRealm(){
AuthorRealm myRealm = new AuthorRealm();
myRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myRealm;
}
/**
* 凭证匹配器
* (由于我们的密码校验交给 shiro 的SimpleAuthenticationInfo进行处理了
* 所以我们需要修改下doGetAuthenticationInfo中的代码;)
* @return HashedCredentialsMatcher
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
logger.info("Shiro: 开启凭证匹配器");
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
// 散列的次数,比如散列两次,相当于 md5(md5(""));
hashedCredentialsMatcher.setHashIterations(1);
return hashedCredentialsMatcher;
}
/**
* 缓存机制
* */
@Bean
public EhCacheManager ehCacheManager(){
logger.info("Shiro: 开启缓存机制");
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
return cacheManager;
}
/**
* Shiro生命周期处理器 * @return
*/
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
logger.info("Shiro: 生命周期处理");
return new LifecycleBeanPostProcessor();
}
/**
* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全
* 逻辑验证 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能 * @return
*/
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
logger.info("Shiro: 开启shiro注解");
DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
proxyCreator.setProxyTargetClass(true);
return proxyCreator;
}
/**
* shiro注解支持
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
logger.info("Shiro: 开启shiro注解支持");
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
SpringBoot官方文档: https://docs.spring.io/spring-boot/docs/current/reference/html/
项目源码: https://gitee.com/ge.yang/SpringBoot