由于网上教程很多,对于shiro的概念和用法已经讲解非常详细,这里直接给出介绍shiro概念的博主连接Shiro笔记(一)----Shiro安全框架简介
以及写的不错的博客:springboot(十四):springboot整合shiro-登录认证和权限管理
SpringBoot整合Shiro
springboot整合shiro-ehcache不错的博文:springboot整合shiro-ehcache缓存(五)
这里是本项目源码,已经上传github,sql文件在doc文件夹里面,感兴趣的小伙伴可以下载 : 去下载
话不多说,先上pom文件:
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.4.RELEASE
com.example
springboot-shiro
0.0.1-SNAPSHOT
springboot-shiro
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.0.1
mysql
mysql-connector-java
runtime
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.apache.shiro
shiro-spring
1.4.0
cn.hutool
hutool-all
4.5.7
org.apache.shiro
shiro-ehcache
1.4.0
org.springframework.boot
spring-boot-maven-plugin
org.mybatis.generator
mybatis-generator-maven-plugin
1.3.7
src/main/resources/mybatis-generator-config.xml
true
true
org.mybatis.generator
mybatis-generator-core
1.3.7
数据库文件:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '权限id',
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限名称',
`permission` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES (1, '用户管理', 'user:list');
INSERT INTO `permission` VALUES (2, '用户添加/更新', 'user:add');
INSERT INTO `permission` VALUES (3, '用户删除', 'user:delete');
INSERT INTO `permission` VALUES (4, '用户编辑页面', 'user:edit');
INSERT INTO `permission` VALUES (5, '文章管理', 'post:list');
INSERT INTO `permission` VALUES (6, '文章查询', 'post:search');
INSERT INTO `permission` VALUES (7, '创建文章页面', 'post:new');
INSERT INTO `permission` VALUES (8, '添加/修改文章', 'post:save');
INSERT INTO `permission` VALUES (9, '编辑文章页面', 'post:edit');
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色id',
`role` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色',
`description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色描述',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'admin', '管理员');
INSERT INTO `role` VALUES (2, 'author', '作者');
INSERT INTO `role` VALUES (3, 'subscriber', '订阅者');
-- ----------------------------
-- Table structure for role_permission_ref
-- ----------------------------
DROP TABLE IF EXISTS `role_permission_ref`;
CREATE TABLE `role_permission_ref` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '关联表role_permission_id',
`role_id` int(11) NOT NULL COMMENT '角色id',
`permission_id` int(11) NOT NULL COMMENT '权限id',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of role_permission_ref
-- ----------------------------
INSERT INTO `role_permission_ref` VALUES (1, 1, 1);
INSERT INTO `role_permission_ref` VALUES (2, 1, 2);
INSERT INTO `role_permission_ref` VALUES (3, 1, 3);
INSERT INTO `role_permission_ref` VALUES (4, 1, 4);
INSERT INTO `role_permission_ref` VALUES (5, 1, 5);
INSERT INTO `role_permission_ref` VALUES (6, 1, 6);
INSERT INTO `role_permission_ref` VALUES (7, 1, 7);
INSERT INTO `role_permission_ref` VALUES (8, 1, 8);
INSERT INTO `role_permission_ref` VALUES (9, 1, 9);
INSERT INTO `role_permission_ref` VALUES (10, 2, 1);
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名',
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码',
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '姓名',
`salt` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '盐值',
`email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱',
`create_time` timestamp(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '创建时间',
`update_time` timestamp(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'tom', '9ade9a62fe19ed021b87f8dff0236ad7', '汤姆', 'n3R2XG', '[email protected]', '2019-05-05 10:38:49', '2019-05-05 10:38:49');
INSERT INTO `user` VALUES (2, 'cat', '5b345ca8ab78e8728cc8448203246496', 'CAT', '1gvEVM', '[email protected]', '2019-05-05 10:39:11', '2019-05-05 10:39:11');
-- ----------------------------
-- Table structure for user_role_ref
-- ----------------------------
DROP TABLE IF EXISTS `user_role_ref`;
CREATE TABLE `user_role_ref` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '关联表user_role_id',
`user_id` int(11) NOT NULL COMMENT '用户id',
`role_id` int(11) NOT NULL COMMENT '角色id',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user_role_ref
-- ----------------------------
INSERT INTO `user_role_ref` VALUES (1, 1, 1);
INSERT INTO `user_role_ref` VALUES (2, 1, 2);
INSERT INTO `user_role_ref` VALUES (3, 1, 3);
INSERT INTO `user_role_ref` VALUES (4, 2, 2);
SET FOREIGN_KEY_CHECKS = 1;
Shiro配置文件
package com.example.springbootshiro.shiro;
import lombok.extern.log4j.Log4j2;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
@Log4j2
public class ShiroConfig {
/**
* ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager){
log.info("进入【shiroFilter】");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//拦截器
Map filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/user/login", "anon");
filterChainDefinitionMap.put("/user/logout", "logout");
//根据用户的角色赋予相应的权限
// filterChainDefinitionMap.put("/add", "roles[admin]");
// filterChainDefinitionMap.put("/delete", "roles[admin]");
// filterChainDefinitionMap.put("/delete", "roles[author]");
filterChainDefinitionMap.put("/addPermission", "roles[author]");
filterChainDefinitionMap.put("/add", "perms[user:add]");
filterChainDefinitionMap.put("/delete", "perms[user:delete]");
filterChainDefinitionMap.put("/userList", "perms[user:list]");
// /** 匹配所有的路径
// 通过Map集合组成了一个拦截器链 ,自顶向下过滤,一旦匹配,则不再执行下面的过滤
// 如果下面的定义与上面冲突,那按照了谁先定义谁说了算
// /** 一定要配置在最后
filterChainDefinitionMap.put("/**", "authc");
// 将拦截器链设置到shiro中
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index");
//未授权页面
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
return shiroFilterFactoryBean;
}
/**
* securityManager
*/
@Bean(name = "securityManager")
public SecurityManager securityManager(
@Qualifier("myShiroRealm") MyShiroRealm myShiroRealm,
@Qualifier("rememberMeManager") CookieRememberMeManager rememberMeManager,
// @Qualifier("cacheManager") CacheManager cacheManager,
@Qualifier("ehCacheManager")EhCacheManager ehCacheManager){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// securityManager.setCacheManager(cacheManager);
securityManager.setCacheManager(ehCacheManager);
securityManager.setRememberMeManager(rememberMeManager);
securityManager.setRealm(myShiroRealm);
return securityManager;
}
/**
* shiroRealm
*/
@Bean(name = "myShiroRealm")
public MyShiroRealm myShiroRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher hashedCredentialsMatcher){
MyShiroRealm myShiroRealm = new MyShiroRealm();
myShiroRealm.setCachingEnabled(true);
//启用身份验证缓存,即缓存AuthenticationInfo信息,默认false
myShiroRealm.setAuthenticationCachingEnabled(true);
//缓存AuthenticationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置
myShiroRealm.setAuthenticationCacheName("authenticationCache");
//启用授权缓存,即缓存AuthorizationInfo信息,默认false
myShiroRealm.setAuthorizationCachingEnabled(true);
//缓存AuthorizationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置
myShiroRealm.setAuthorizationCacheName("authorizationCache");
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
return new MyShiroRealm();
}
/**
* 密码匹配凭证管理器
*
* @return
*/
@Bean(name = "hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
log.info("hashedCredentialsMatcher()");
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(1024);// 散列的次数,比如散列两次,相当于md5(md5(""));
return hashedCredentialsMatcher;
}
/**
* cookie对象
* @return
*/
@Bean(name = "rememberMeCookie")
public SimpleCookie rememberMeCookie(){
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
simpleCookie.setHttpOnly(true);
//设置有效期时间30天
simpleCookie.setMaxAge(259200);
return simpleCookie;
}
@Bean(name = "rememberMeManager")
public CookieRememberMeManager rememberMeManager(@Qualifier("rememberMeCookie")SimpleCookie rememberMeCookie){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie);
//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
return cookieRememberMeManager;
}
/**
* 开启shiro aop注解支持
* 使用代理方式;所以需要开启代码支持
* @param securityManager
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 开启cglib代理
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
// 缓存配置
//shiro自带的MemoryConstrainedCacheManager作缓存
// 但是只能用于本机,在集群时就无法使用,需要使用ehcache
@Bean(name = "cacheManager")
public CacheManager cacheManager() {
MemoryConstrainedCacheManager cacheManager=new MemoryConstrainedCacheManager();//使用内存缓存
return cacheManager;
}
//配置ehcache
@Bean(name = "ehCacheManager")
public EhCacheManager ehCacheManager(){
EhCacheManager ehCacheManager = new EhCacheManager();
ehCacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
return ehCacheManager;
}
//在用spring管理我们的类的时候有时候希望有些属性值是来源于一些配置文件,系统属性,或者一些方法调用的结果,
// 对于前两种使用方式可以使用spring的PropertyPlaceholderConfigurer类来注入,
// 对于后一种则可以使用org.springframework.beans.factory.config.MethodInvokingFactoryBean类来生成需要注入的bean的属性。
// 通过MethodInvokingFactory Bean类,可注入方法返回值。
// MethodInvokingFactoryBean用来获得某个方法的返回值,该方法既可以是静态方法,也可以是实例方法。
// 该方法的返回值可以注入bean实例属性,也可以直接定义成bean实例
//可查看http://blog.sina.com.cn/s/blog_72ef7bea0102wa0v.html
/**
* 让某个实例的某个方法的返回值注入为Bean的实例
* Spring静态注入
* @param myShiroRealm
* @param rememberMeManager
* @param ehCacheManager
* @return
*/
@Bean(name = "methodInvokingFactoryBean")
public MethodInvokingFactoryBean methodInvokingFactoryBean(
@Qualifier("myShiroRealm") MyShiroRealm myShiroRealm,
@Qualifier("rememberMeManager") CookieRememberMeManager rememberMeManager,
@Qualifier("ehCacheManager")EhCacheManager ehCacheManager){
MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
factoryBean.setArguments(new Object[]{securityManager(myShiroRealm, rememberMeManager,ehCacheManager)});
return factoryBean;
}
}
自定义Realm:
package com.example.springbootshiro.shiro;
import cn.hutool.core.lang.Validator;
import com.example.springbootshiro.entity.Permission;
import com.example.springbootshiro.entity.Role;
import com.example.springbootshiro.entity.User;
import com.example.springbootshiro.service.PermissionService;
import com.example.springbootshiro.service.RoleService;
import com.example.springbootshiro.service.UserService;
import lombok.extern.log4j.Log4j2;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
@Log4j2
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Autowired
private PermissionService permissionService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
log.info("进入授权【doGetAuthorizationInfo】");
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
User user = (User) principals.getPrimaryPrincipal();
List roleList = roleService.getRoleListByUserId(user.getId());
for (Role role : roleList) {
//添加角色
authorizationInfo.addRole(role.getRole());
List permissionList = permissionService.getPermissionListByRoleId(role.getId());
for (Permission permission : permissionList) {
//添加权限
authorizationInfo.addStringPermission(permission.getPermission());
}
}
return authorizationInfo;
}
/**
* 登录认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//加入这一步的目的是在post请求的时候会先认证,然后再请求
if (authenticationToken.getPrincipal() == null){
return null;
}
log.info("进入认证【doGetAuthenticationInfo】");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
User user = null;
if (Validator.isEmail(username)){
user = userService.getUserByEmail(username);
}else {
user = userService.getUserByUserName(username);
}
if (user == null){
log.info("用户不存在!");
return null;
}
//封装AuthenticationInfo,准备验证
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
user, user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName());
return info;
}
/**
* 重写方法,清除当前用户的的 授权缓存
* @param principals
*/
@Override
public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
super.clearCachedAuthorizationInfo(principals);
}
/**
* 重写方法,清除当前用户的 认证缓存
* @param principals
*/
@Override
public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
super.clearCachedAuthenticationInfo(principals);
}
@Override
public void clearCache(PrincipalCollection principals) {
super.clearCache(principals);
}
/**
* 自定义方法:清除所有 授权缓存
*/
public void clearAllCachedAuthorizationInfo() {
getAuthorizationCache().clear();
}
/**
* 自定义方法:清除所有 认证缓存
*/
public void clearAllCachedAuthenticationInfo() {
getAuthenticationCache().clear();
}
/**
* 自定义方法:清除所有的 认证缓存 和 授权缓存
*/
public void clearAllCache() {
clearAllCachedAuthenticationInfo();
clearAllCachedAuthorizationInfo();
}
/**
* 加密
* @param args
*/
public static void main(String[] args) {
String hashAlgorithName = "MD5";
String password = "123";
int hashIterations = 1024;
ByteSource credentialsSalt = ByteSource.Util.bytes("tom");
Object obj = new SimpleHash(hashAlgorithName, password, credentialsSalt, hashIterations);
log.info(obj);
}
}
在resources下面新建config文件夹,并创建ehcache-shiro.xml文件
主要的配置文件都在这里了。
欢迎大家讨论,如果程序出现错误,轻喷~~