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接口。