Shiro
Shiro 三大对象
Subject 用户
SecurityManager 管理所有用户
Realm 连接数据
1,导入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.2</version>
</dependency>
2,编写config
@Configuration
public class ShiroConfig {
// 3.创建
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 设置 安全管理器
factoryBean.setSecurityManager(defaultWebSecurityManager);
// 添加shiro的内置过滤器
/*
* anon: 无需认证就可以访问
* authc: 认证才能访问
* user: 必须拥有 记住我功能 才能使用
* perms: 拥有对某个资源的权限时才能访问
* role: 拥有某个角色权限才能访问
* */
// 拦截
Map<String, String> filterMap = new LinkedHashMap<>();
// 无需认证就可以访问
filterMap.put("/css/**","anon");
// 授权 没有授权会跳转到授权页面
// 用户必须有publish权限才可以进入
filterMap.put("/admin/publish","perms[user:publish]");
//用户必须有our权限才可以进入
filterMap.put("/admin/our","perms[user:our]");
factoryBean.setFilterChainDefinitionMap(filterMap);
// 设置登录页
factoryBean.setLoginUrl("/");
factoryBean.setLoginUrl("/login");
// 未授权页面
factoryBean.setUnauthorizedUrl("/admin/noAuth");
// 等~~~~
return factoryBean;
}
// 2.创建 DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(
@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
//关联 UserRealm
manager.setRealm(userRealm);
return manager;
}
// 1.创建 realm 对象
@Bean(name = "userRealm")
public UserRealm userRealm(){
return new UserRealm();
}
}
3,自定义的 UserRealm
/* 自定义的 UserRealm */
public class UserRealm extends AuthorizingRealm {
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principalCollection) {
return null;
}
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
return null;
}
}
4,Controller 中
//获取当前用户--- SecurityUtils 是shiro包中的
Subject subject = SecurityUtils.getSubject();
// 封装用户登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username.trim(), password);
// 执行 subject.login 就会到 UserRealm 中的认证操作
// 没有异常则成功
try {
subject.login(token);
return "redirect:/admin/index";
}catch (UnknownAccountException e){//用户名不正确
model.addAttribute("msg","用户名错误");
return "login";
}catch ( IncorrectCredentialsException e){//密码错误
model.addAttribute("msg","密码错误");
return "login";
}
5,UserRealm 的==认证==代码块
// 得到封装的用户数据
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
// 根据用户在数据库中查找数据--shiro会自动比较用户输入的密码和查询的用户的密码是否一致
// 密码一致则成功 否则 返回密码错误异常(IncorrectCredentialsException)
// 密码认证 由shiro做 密码加密可在 controller中设置
// SimpleAuthenticationInfo 的参数
// 查找出的用户,从数据库取出来的明文密码,(加密明文所用的盐对象(ByteResource),此Realm的名称)
return new SimpleAuthenticationInfo("查找出的用户","查找出来的用户密码","");
6,UserRealm 的==授权==代码块
// SimpleAuthorizationInfo授权; SimpleAuthenticationInfo认证
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Subject subject = SecurityUtils.getSubject();
// 拿到user对象--拿到的是由认证返回的用户对象
User currentUser = (User) subject.getPrincipal();
// 设置当前用户得权限--权限名称可以从数据库中取
info.addStringPermission("xxx");
7,整合Thymeleaf 添加依赖
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
8,shiroconfig 中添加
/*
* 配置ShiroDialect 用于 thymeleaf和shiro标签配合使用
* */
@Bean
public ShiroDialect getShiroDia(){
return new ShiroDialect();
}
页面中导入标签
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"
// 是否拥有这个权限 有则显示~
shiro:hasPermission="user:publish"
9,密码加密
md5 盐值加密
public class MD5SaltUtil {
public static String MD5Salt(String salt,String obj){
ByteSource bytes = ByteSource.Util.bytes(salt);
SimpleHash simpleHash=new SimpleHash("MD5", obj, salt, 1024);
return simpleHash.toString();
}
}
// md5 盐值 解密 加密后的密码
String simpleHash = MD5SaltUtil.MD5Salt(name, password);
认证中的代码
/*SimpleAuthenticationInfo(用户名/用户,密码,盐值,当前的Realm)*/
SimpleAuthenticationInfo info = new
SimpleAuthenticationInfo(one, one.getUPassword(),ByteSource.Util.bytes(one.getUName()), getName());
shiroconfig
@Bean("hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//指定加密方式为MD5
credentialsMatcher.setHashAlgorithmName("MD5");
//加密次数
credentialsMatcher.setHashIterations(1024);
return credentialsMatcher;
}
10,Shiro 记住我 session保存Redis
页面
<span class="txt1">记住我</span>
<input type="checkbox" name="rememberMe" checked />
Controller
@GetMapping({"/","login"})
public String login(HttpSession session){
System.err.println("----记住我状态---->"+SecurityUtils.getSubject().isRemembered());
if(SecurityUtils.getSubject().isRemembered()){
Subject currentUser = SecurityUtils.getSubject();
User user = (User) currentUser.getPrincipal();
session.setAttribute("user", user);
return "index";
}else {
return "login";
}
}
登录的时候在UserRealm
中的认证中把用户存到session
中
// 得到封装的用户数据
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUname,userToken.getUsername()).or().
eq(User::getUemail,userToken.getUsername()).or().
eq(User::getUphone,userToken.getUsername());
User one = userService.getOne(wrapper);
// 用户存到session中
Subject subject = SecurityUtils.getSubject();
subject.getSession().setAttribute("user",one);
ShiroConfig 配置
package com.yifan.config;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.mgt.SessionManager;
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.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
private Logger logger = LoggerFactory.getLogger(ShiroConfig.class);
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.timeout}")
private Integer timeout;
@Bean
public SimpleCookie rememberMeCookie(){
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//cookie生效时间7天,单位秒;
simpleCookie.setMaxAge(7*24*60*60);
simpleCookie.setName("rememberMe");
return simpleCookie;
}
@Bean
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
// cookieRememberMeManager.setCipherKey用来设置加密的Key,参数类型byte[],字节数组长度要求16
// cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));
cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
return cookieRememberMeManager;
}
@Bean("myCacheManager")
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
//redis中针对不同用户缓存 传入的是用户的字段
// 否则报错 class com.yifan.entity.User must has getter for field: id
redisCacheManager.setPrincipalIdFieldName("uid");
return redisCacheManager;
}
// 它可对redis的ip、端口等进行配置
@Bean
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setTimeout(timeout);
return redisManager;
}
/**
* RedisSessionDAO shiro sessionDao层的实现 通过redis
* 使用的是shiro-redis开源插件
*/
@Bean
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
/**
* 配置会话管理器,设定会话超时及保存
* @return
*/
//自定义sessionManager
@Bean("sessionManager")
public SessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
// 取消登录成功后url 后面的 JSESSIONID
sessionManager.setSessionIdUrlRewritingEnabled(false);
sessionManager.setSessionDAO(redisSessionDAO());
return sessionManager;
}
/** 告诉 shiro 是经过加密的
* 密码校验规则HashedCredentialsMatcher
* 这个类是为了对密码进行编码的 ,
* 防止密码在数据库里明码保存 , 当然在登陆认证的时候 ,
* 这个类也负责对form里输入的密码进行编码
* 处理认证匹配处理器:如果自定义需要实现继承HashedCredentialsMatcher
*/
@Bean("hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//指定加密方式为MD5
credentialsMatcher.setHashAlgorithmName("MD5");
//加密次数
credentialsMatcher.setHashIterations(1024);
// credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}
// 3.创建 Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 设置 安全管理器
factoryBean.setSecurityManager(defaultWebSecurityManager);
// 添加shiro的内置过滤器
/*
* anon: 无需认证就可以访问
* authc: 认证才能访问
* user: 必须拥有 记住我功能 才能使用
* perms: 拥有对某个资源的权限时才能访问
* role: 拥有某个角色权限才能访问
* */
// 拦截
Map<String, String> map = new LinkedHashMap<>();
// 无需认证就可以访问
// map.put("/static/**","anon"); 这个过滤不了
map.put("/css/**","anon");
map.put("/img/**","anon");
map.put("/js/**","anon");
map.put("/fonts/**","anon");
map.put("/webfonts/**","anon");
map.put("/user/movie/getMData","anon");
map.put("/tools/FanYi","anon");
map.put("/tools/AddWords","anon");
map.put("/tools/translate","anon");
map.put("/tools/movie/mPlayer","anon");
map.put("/toIndex","anon");
map.put("/**","user");
factoryBean.setFilterChainDefinitionMap(map);
// // 设置登录页
factoryBean.setLoginUrl("/");
factoryBean.setLoginUrl("/login");
// 未授权页面
factoryBean.setUnauthorizedUrl("/noAuth");
// 等~~~~
return factoryBean;
}
// 2.创建 DefaultWebSecurityManager 权限管理,配置主要是Realm的管理认证
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(
@Qualifier("userRealm") UserRealm userRealm){
logger.info("- - - - - - -shiro开始加载- - - - - - ");
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
//关联 UserRealm
manager.setRealm(userRealm);
// 自定义session管理 使用redis
manager.setSessionManager(sessionManager());
// 自定义缓存实现 使用redis
manager.setCacheManager(cacheManager());
// 使用记住我
manager.setRememberMeManager(rememberMeManager());
return manager;
}
// 1.创建 realm对象 把 hashedCredentialsMatcher 注册进来~
@Bean(name = "userRealm")
public UserRealm userRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher credentialsMatcher){
UserRealm userRealm = new UserRealm();
userRealm.setCredentialsMatcher(credentialsMatcher);
return userRealm ;
}
}
11,基础5表+Shiro 项目-> boot-shiro
1、用户表(UserInfo):Id、UserName、UserPwd 、URole
2、角色表(RoleInfo):Id、RoleName
3、菜单(权限)表(MenuInfo):Id、MenuName
4、用户角色表(UserRole):Id、UserId、RoleId
5、角色菜单表(RoleMenu):Id、RoleId、MenuId
UserRealm
// 授权 设置角色和权限 使用的是 mybatis
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
logger.info("--------------------授权--------------------");
// SimpleAuthorizationInfo授权; SimpleAuthenticationInfo认证
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 拿到user对象--拿到的是由认证返回的用户对象
User currentUser = (User) principal.getPrimaryPrincipal();
// 根据当前用户查出用户角色
List<Userrole> userRoles = userroleService.getUserRole(currentUser.getUId());
Set<String> roleSet = new HashSet<>();
Set<String> permissSet = new HashSet<>();
for (Userrole userRole : userRoles) {
Role role = roleService.getRole(userRole.getRoleId());
roleSet.add(role.getRName());
// 根据用户角色查用户权限
List<Rolemenu> list = rolemenuService.list(userRole.getRoleId());
for (Rolemenu rolemenu : list) {
Integer menuId = rolemenu.getMenuId();
Menu usermenu = menuService.getOne(menuId);
permissSet.add(usermenu.getMName());
}
}
// 添加用户角色
info.setRoles(roleSet);
// set.add("addUser");
// set.add("delUser");
// set.add("selUser");
// set.add("updUser");
// 添加用户菜单权限
info.setStringPermissions(permissSet);
System.out.println("------> "+roleSet);
System.out.println(permissSet);
return info;
}
ShiroConfig
map.put("/user/addUser","perms[addUser]");
map.put("/user/delUser","perms[delUser]");
map.put("/user/selUser","perms[selUser]");
map.put("/user/updUser","perms[updUser]");
//用户,需要角色权限 “user”
map.put("/user/**", "roles[user]");
//管理员,需要角色权限 “admin”
map.put("/admin/**", "roles[admin]");
sql语句
/*
Navicat MySQL Data Transfer
Source Server : 1212
Source Server Version : 80017
Source Host : localhost:3306
Source Database : db01
Target Server Type : MYSQL
Target Server Version : 80017
File Encoding : 65001
Date: 2020-07-31 16:23:13
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for menu
-- ----------------------------
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu` (
`m_id` int(11) NOT NULL AUTO_INCREMENT,
`m_name` varchar(255) DEFAULT NULL COMMENT '权限菜单名称',
PRIMARY KEY (`m_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of menu
-- ----------------------------
INSERT INTO `menu` VALUES ('1', 'addUser');
INSERT INTO `menu` VALUES ('2', 'delUser');
INSERT INTO `menu` VALUES ('3', 'selUser');
INSERT INTO `menu` VALUES ('4', 'updUser');
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`r_id` int(11) NOT NULL AUTO_INCREMENT,
`r_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`r_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('1', 'admin');
INSERT INTO `role` VALUES ('2', 'user');
-- ----------------------------
-- Table structure for rolemenu
-- ----------------------------
DROP TABLE IF EXISTS `rolemenu`;
CREATE TABLE `rolemenu` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_id` int(11) DEFAULT NULL,
`menu_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of rolemenu
-- ----------------------------
INSERT INTO `rolemenu` VALUES ('1', '1', '1');
INSERT INTO `rolemenu` VALUES ('2', '1', '2');
INSERT INTO `rolemenu` VALUES ('3', '1', '3');
INSERT INTO `rolemenu` VALUES ('4', '1', '4');
INSERT INTO `rolemenu` VALUES ('5', '2', '3');
INSERT INTO `rolemenu` VALUES ('6', '2', '4');
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`u_id` int(11) NOT NULL AUTO_INCREMENT,
`u_name` varchar(255) DEFAULT NULL,
`u_password` varchar(255) DEFAULT NULL,
PRIMARY KEY (`u_id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', '123', '9c3b5c0672cd599ccf1019bddaa8089b');
INSERT INTO `user` VALUES ('2', 'tom', '660d9c5997ff08ba6d1961d9ddd8ee73');
INSERT INTO `user` VALUES ('3', 'jack', '42df4a630b4b9915e8bcc0ef2d58c0eb');
INSERT INTO `user` VALUES ('11', 'yifan', '2eba8a45d2053c038a882a94bdc1322e');
-- ----------------------------
-- Table structure for userrole
-- ----------------------------
DROP TABLE IF EXISTS `userrole`;
CREATE TABLE `userrole` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`role_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of userrole
-- ----------------------------
INSERT INTO `userrole` VALUES ('1', '1', '1');
INSERT INTO `userrole` VALUES ('2', '2', '2');
INSERT INTO `userrole` VALUES ('3', '3', '2');
INSERT INTO `userrole` VALUES ('8', '11', '2');
INSERT INTO `userrole` VALUES ('9', '1', '2');
12,redis 缓存权限
用户进行权限验证时 Shiro会去缓存中找,如果查不到数据,会执行doGetAuthorizationInfo这个方法去查权限,并放入缓存中
ShiroConfig
/**
* RedisSessionDAO shiro sessionDao层的实现 通过redis
* 使用的是shiro-redis开源插件
*/
@Bean
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
/**
* Session Manager
* 使用的是shiro-redis开源插件
*/
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
//取消url 后面的 JSESSIONID
sessionManager.setSessionIdUrlRewritingEnabled(false);
return sessionManager;
}
/**
* 配置shiro redisManager
* 使用的是shiro-redis开源插件
*
* @return
*/
@Bean
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setTimeout(timeout);
return redisManager;
}
/**
* cacheManager 缓存 redis实现
* 使用的是shiro-redis开源插件
*
* @return
*/
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
/*--------------------------------------------*/
redisCacheManager.setPrincipalIdFieldName("uId");
return redisCacheManager;
}
//将缓存注入安全管理器,就不会反复执行 realm的授权方法了;只要实现了shiro的cache接口、CacheManager接口就可以用来注入安全管理器
//shiro自带的一个内存缓存,本质是hashmap,MemoryConstrainedCacheManager(),试验没问题,非常轻,简单的登录用这个
manager.setCacheManager(cacheManager());
//配置 redisSession管理
manager.setSessionManager(sessionManager());
pom
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-cache</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.2.3</version>
</dependency>
<!-- shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
<!-- thymeleaf整合shiro标签 -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
日夜颠倒头发少 ,单纯好骗恋爱脑 ,会背九九乘法表 ,下雨只会往家跑 ,搭讪只会说你好 ---- 2050781802@qq.com