当前位置: 首页 > news >正文

Shiro安全框架简介

 一、权限管理

1.1 什么是权限管理

  • 基本上只要涉及到用户参数的系统都要进行权限管理,使用权限管理实现了对用户访问系统的控制,不同的用户访问不同的资源。按照安全规则或者安全策略控制用户访问资源,而且只能访问被授权的资源
  • 权限管理包括认证和授权两部分,当用户访问资源时先对其进行身份的认证,认证通过后即可访问已经授权的资源。

1.2 身份认证

  • 用来判断一个用户是否合法的处理过程。用过用户输入的用户名或者口令来和系统中存储进行比较,从而认证用户的身份是否正确

对于身份认证,也就是之前做的的登录

1.3 授权

  • 用来控制认证过后的用户可以访问哪些资源。用户身份认证后需要给该用户分配可访问的资源,如果没有某个资源的权限,那么将无法访问

二、Shiro架构

2.1 Shiro的理解

是一个功能强大且易实现的Java安全框架,使用Shiro可以执行认证、授权、加密和会话管理。使用Shiro中提供的API可以快速轻松的保护任何程序

Shiro不依赖于WEB,即使是一个测试程序也能够使用Shiro中的功能

2.2 Shiro的体系

官方图示:

原理:

Subject

  • 表示主体,外部应用和Subject进行交互。Subject中记录了当前操作用户,这个用户可以是一个发送请求的用户,也可以是一个运行的程序
  • Subject在Shiro是一个接口,定义了很多认证授权的相关方法,外部程序通过Subject进行认证授权,Subject又是通过SecurityManager安全管理器就行认证管理

SecurityManager

  • 表示安全管理器,是Shiro中的核心,用来协调其托管的组件,以保证它们能够顺利协同工作。
  • 可以进行会话管理等

Authenticator

  • 表示身份认证器,负责执行和响应用户的身份认证尝试的组件。当用户进行登录操作时,此组件进行处理

Authorizer

  • 表示授权器,负责控制用户在系统中可以访问哪些资源。在访问资源时都需要该组件进行判断当前用户是否拥有这个资源的权限

Realm

  • 表示领域,充当Shiro与应用程序的安全数据之间的桥梁或者连接器,和DataSource数据源差不多,当需要和安全相关的数据(如用户账号)进行实际交互从而执行认证和授权时,Shiro会从应用配置的一个或者多个Realm中查询其中的内容
  • SecurityManager进行安全认证时需要通过Realm获取到用户权限数据
  • Realm不只是从数据库取数据,还有认证和授权的相关逻辑代码

SessionManager

  • 表示会话管理,知道如果创建和管理用户生命周期,以便为所欲环境中的用户提供强大的会话体验
  • 不依赖WEB容器,所以Shiro可以使用在非WEB应用中也可以将分布式应用的会话集中在一点管理,次特征可以使它实现单点登录

SessionDao

  • 表示会话Dao,是对session会话操作的一套接口

CacheManager

  • 表示缓存管理器,将用户权限存储到缓存中,从而提高性能

Cryptography

  • 表示密码管理,Shiro中提供了一套加密/解密的组件,方便开发

三、Shiro中的认证

3.1 认证中的关键对象

  • Subject:主体

访问系统的每一个用户或者应用程序,经过认证的都成为主体

  • Principal:身份信息

是主体(Subject)进行身份证认证的表示,表示必须具有唯一性。比如用户名/手机号/邮箱,一个主体(Subject)中可以有多个身份信息,但必须有一个主身份

  • Credential:凭证信息

3.2 认证流程

图示:

文字:

  1. 收集使用者的Principal和Credential
  1. 提交进行身份验证
  1. 如果验证成功就允许访问,否则重试身份证验证或者阻止访问

3.3 环境搭建

  1. 引入Shiro依赖
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.9.1</version>
</dependency>
  1. 在resources下创建.ini的配置文件,来临时模拟数据库存储用户的身份信息和凭证信息

[users]
admin=1234
tom=222
jack=456

  1. 编写认证代码
public class ShiroAuthentication {public static void main(String[] args) {// 1.创建SecurityManager安全管理器的实现类DefaultSecurityManager securityManager = new DefaultSecurityManager();// 2.将Realm中的数据设置到安全管理器中securityManager.setRealm(new IniRealm("classpath:shiro.ini")); // Realm去读取ini中的主体与凭证信息// 3.将安全管理器设置到全局安全工具类中SecurityUtils.setSecurityManager(securityManager);// 4.获取主体Subject subject = SecurityUtils.getSubject();// 5.认证if (!subject.isAuthenticated()) { // 是否已经认证// 如果没有认证过,那么证明该用户第一次登录.收集使用者的身份信息和凭证信息UsernamePasswordToken token = new UsernamePasswordToken("admin","1234"); // 模拟前台输入// 提交身份信息和凭证信息subject.login(token);}}
}

3.4 处理结果

如果提交成功,执行后续的逻辑代码;提交过程中出现错误,那么Shiro将以抛异常的形式声明错误

异常列表:

  • UnknowAccountException  --> 未知账号异常【用户名错误】
  • IncorrectCredentialsException  --> 凭证信息异常【密码错误】
  • LockedAccountException  --> 锁定账号异常
  • ExcessiveAttemptsException  --> 过度尝试异常
  • AuthenticationException  --> 身份认证异常

修改以上程序:

try {// 开始认证subject.login(token);
}catch (UnknownAccountException e){System.out.println("账号错误");
}catch (IncorrectCredentialsException e){System.out.println("密码错误");
}

3.5 获取主体身份信息与注销

此操作必须保证认证通过

  • 获取主体身份信息
if (subject.isAuthenticated()){ // 认证通过Object principal = subject.getPrincipal(); // 6. 获取身份信息System.out.println(principal); // admin
}

  • 注销
subject.logout();

3.6 底层实现

身份信息校验

  • 在SimpleAccountRealm类中的doGetAuthenticationInfo方法判断身份信息是否一致
  • 如果用户名错误,那么返回的info==null,系统抛出UnknowAccountException异常

密码校验

  • 在AuthenticatingRealm类中的assertCredentialsMatch方法进行密码(凭证信息)的校验,Shiro中凭证信息默认的校验规则是equals
  • 如果密码错误,抛出IncorrectCredentialsException异常

3.7 自定义Realm

以后校验用户名肯定不能使用Shiro中定义的,需要连接数据库,通过用户名查询。所以Shiro中也可以让我们自定义Realm

Realm继承图

全部

主要部分

认证方法和授权方法都在AuthorizingRealm定义为抽象方法,等待子类继承并重写这两个方法。

SimpleAccountRealm继承了AuthorizingRealm,所以这个类里面有认证和授权功能,其两个功能对应的方法为:

  • doGetAuthenticationInfo:认证
  • doGetAuthorizationInfo:授权

以后自定义的Realm只需要继承AuthorizingRealm,然后重写这两个方法

/**
* 自定义Realm,继承AuthorizingRealm类
*/
public class LoginRealm extends AuthorizingRealm {/*授权*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {return null;}/*认证*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {return null;}
}

在doGetAuthenticationInfo方法中获取用户的身份信息,然后校验是否和数据库中的一致

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {// getPrincipal()获取身份信息String username = (String) token.getPrincipal();if ("admin".equals(username)){// 正确 返回AuthenticationInfo的实现类// 参数:1.当前用户的身份信息 2.验证主体的凭证信息[如果和前台传入的不一致,抛出IncorrectCredentialsException异常] 3.当前Realmreturn new SimpleAuthenticationInfo(username,"1234",super.getName());}// 不正确返回一个null, info == null 抛出UnknownAccountException异常return null;
}

3.8 加密

测试程序:

public class MD5Test {public static void main(String[] args) {// 加密Md5Hash md5Hash = new Md5Hash("1234");// 加密+salt(盐)Md5Hash md5Hash1 = new Md5Hash("1234", "f5gy");// 加密+salt(盐)+散列次数Md5Hash md5Hash2 = new Md5Hash("1234","f5gy",1024);}
}

整合认证:

  1. 修改认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {// getPrincipal()获取身份信息String username = (String) token.getPrincipal();if ("admin".equals(username)){// 正确 返回AuthenticationInfo的实现类/*参数:1.当前用户的身份信息2.验证主体的凭证信息[如果和前台传入的不一致,抛出IncorrectCredentialsException异常]3.盐4.当前Realm*/String password = "75b323294effa42ed07f895f37f9a192";String salt = "f5gy";return new SimpleAuthenticationInfo(username,password, ByteSource.Util.bytes(salt),super.getName());}// 不正确返回一个null, info == null 抛出UnknownAccountException异常return null;
}

  1. 修改密码比较器

Shiro默认实用的是simpleCredentialsMatcher中的doCredentialsMatcher方法,这个方法使用的是equals的方式进行比较密码。

CredentialsMatcher继承图:

使用HashedCredentialsMatcher这个类

LoginRealm realm = new LoginRealm();
HashedCredentialsMatcher hash = new HashedCredentialsMatcher();
// 设置算法
hash.setHashAlgorithmName("MD5");
// 设置散列次数
hash.setHashIterations(1024);
// 设置到Realm中
realm.setCredentialsMatcher(hash);

四、Shiro中的授权

4.1 授权中的关键对象

  • Who

表示主体,主题需要系统中的资源

  • What

表示资源,这个资源可以是一个按钮、菜单等。资源又分为资源实例和资源类型

  • How

表示权限/许可,控制主体对资源的访问

4.2 授权方式

  • 基于角色的访问控制(Role-Based Access Control):以角色为中心进行权限控制
  • 基于资源的访问控制(Resource-Based Access Control):以资源为中心进行权限控制

4.3 权限字符串

权限字符串的规则:资源标识符:操作,意思是对哪个资源进行哪些操作。":"是分割符,权限字符串可以使用"*"来表示通配符

4.4 校验角色

  1. 在doGetAuthorizationInfo方法中设置当前主体的角色
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {// 获取主题中的身份信息[用户名]String principal = (String) principals.getPrimaryPrincipal();// 返回AuthorizationInfo的实现类SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();// 给当前主体添加角色info.addRole("admin");info.addRole("user");return info;
}

  1. 模拟前台测试
if (subject.isAuthenticated()){// 校验单个角色System.out.println(subject.hasRole("admin")); // 是否有admin角色// 校验多个角色System.out.println(subject.hasAllRoles(Arrays.asList("admin", "user"))); // 是否同时有admin user角色// 校验 多次 角色boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "user", "super"));for (boolean b : booleans) {System.out.println(b);}
}

4.5 校验权限字符串

  1. 在doGetAuthorizationInfo方法中设置当前主体的权限字符串
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {// 获取主题中的身份信息[用户名]String principal = (String) principals.getPrimaryPrincipal();// 返回AuthorizationInfo的实现类SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();// 给当前主体添加权限字符串info.addStringPermission("user:update");info.addStringPermission("product:select");return info;
}

  1. 模拟前台测试

五、整合SpringBoot

5.1 整合思路

5.2 环境搭建

  1. 导入springboot和shiro整合的依赖包
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-starter</artifactId><version>1.4.0</version>
</dependency>
  1. 创建一个类,继承AuthorizingRealm类,重写授权和认证方法
public class MyRealm extends AuthorizingRealm {@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {return null;}
}
  1. 编写shiro和springboot整合的配置
@Configuration
public class ShiroConfig {@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();// 将SecurityManager设置到Filter中shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);return shiroFilterFactoryBean;}@Beanpublic DefaultWebSecurityManager defaultWebSecurityManager(Realm realm){DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();// 给安全管理器设置RealmdefaultWebSecurityManager.setRealm(realm);return defaultWebSecurityManager;}@Beanpublic Realm Realm(){LoginRealm loginRealm = new LoginRealm();return loginRealm;}
}
  1. 在ShiroFilter过滤器中配置需要拦截的资源URL
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();// 将SecurityManager设置到Filter中shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);//设置受限资源Map<String,String> map = new HashMap<>();/*** authc:该路径资源需要认证和授权*/map.put("/**","authc"); // "/**"代表所有的资源路径都拦截shiroFilterFactoryBean.setFilterChainDefinitionMap(map);return shiroFilterFactoryBean;
}
  1. 访问资源跳转到login.jsp,修改默认跳转路径
shiroFilterFactoryBean.setLoginUrl("/doLogin");@Controller
public class IndexController {@GetMapping("/doLogin")public String doLogin(){return "login";}
}
  1. 拦截后访问/doLogin路径

5.3 ShiroFilter过滤列表

Shiro中提供了多个默认的过滤器,用这些过滤器来控制指定URL路径下的资源

配置缩写

对应过滤器

描述

anon

AnonymousFilter

指定URL路径下的资源可以匿名访问

authc

FormAuthenticationFilter

指定URL路径下的资源需要认证过后才能访问

authcBasic

BasicHttpAuthenticationFilter

指定URL路径下的资源需要basic登录

logout

LogoutFilter

注销过滤器,只需配置对应的URL路径即可实现

noSessionCreation

NoSessionCreationFilter

禁止创建Session会话

perms

PermissionsAuthorizationFilter

需要有该URL资源对应的权限字符串才能访问

port

PortFilter

指定某个端口可以访问

rest

HttpMethodPermissionFilter

将HTTP请求转换成相对应的动词来构建权限字符串

roles

RolesAuthorizationFilter

需要有指定角色才能访问

ssl

SslFilter

需要https请求才能访问

user

UserFilter

需要已登录或者"记住我"的用户才能访问

六、连接数据库完成认证

6.1 注册

  1. 表设计

  1. 注册页面
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><h1>注册页面</h1><hr><form action="/user/register" method="post">用户名 : <input type="text" name="username"><br>密码 : <input type="password" name="password"> <br><input type="submit" value="注册"></form></body>
</html>
  1. 实体类POJO
@Data
public class User {private Long id;private String username;private String password;private String salt;
}
  1. 编写Controller,调用service处理业务逻辑
@Controller
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/doRegister")public String doRegister(){return "register";}@PostMapping("/register")public String register(User user){int count = userService.register(user);if (count > 0)return "redirect:/doLogin";elsereturn "redirect:/doRegister";}
}
  1. 在ShiroFilter放过这些URL路径
map.put("/user/register","anon");
map.put("/user/doRegister","anon");
  1. service中完成加密和散列盐,调用Mapper完成注册
@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;@Overridepublic int register(User user) {String salt = "ga*n";Md5Hash md5Hash = new Md5Hash(user.getPassword(),salt,1024);user.setPassword(md5Hash.toHex());user.setSalt(salt);return userMapper.insert(user);}
}
  1. Mapper接口和SQL语句
public interface UserMapper {int insert(User user);
}
<mapper namespace="com.jiuxiao.mapper.UserMapper"><insert id="insert">insert into t_user(id,username,password,salt) values(null,#{username},#{password},#{salt})</insert>
</mapper>
  1. 启动类上添加@MapperScan注解,扫描Mapper包
@SpringBootApplication
@MapperScan("com.jiuxiao.mapper")
public class ShiroApp {public static void main(String[] args) {SpringApplication.run(ShiroApp.class,args);}
}
  1. 注册页面输入用户信息后,完成注册

6.2 认证

  1. 在Shiro配置中修改密码比较器
@Bean
public Realm realm(){LoginRealm loginRealm = new LoginRealm();HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();matcher.setHashAlgorithmName("MD5");matcher.setHashIterations(1024);loginRealm.setCredentialsMatcher(matcher);return loginRealm;
}
  1. 在自定义的Realm的doGetAuthenticationInfo方法中编写认证逻辑
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {@Autowiredprivate UserService userService;// 获取主体中的身份信息String principal = (String) authenticationToken.getPrincipal();// 调用service查询数据库User user = userService.selectByUsername(principal);if (!ObjectUtils.isEmpty(user)){// 如果可以查询到,校验密码return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),super.getName());}// 查询不到直接返回nullreturn null;
}
  1. service调用Mapper
@Override
public User selectByUsername(String principal) {return userMapper.selectByUsername(principal);
}
  1. Mapper接口与SQL语句
User selectByUsername(String principal);
<select id="selectByUsername" resultType="com.jiuxiao.pojo.User">select * from t_user where username = #{username}
</select>
  1. 登录页面代码
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>$Title$</title></head><body><h1>登录页面</h1><hr><form action="/user/login" method="post">用户名: <input type="text" name="username"> <br>密码: <input type="password" name="password"> <br><input type="submit" value="登录"></form></body>
</html>
  1. 在Controller中编写对应URL,封装token,并处理异常结果
@Controller
@RequestMapping("/user")
public class UserController {@PostMapping("/login")public String login(String username,String password){Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(username, password);try {subject.login(token);return "index"; // 登陆成功跳转到首页} catch (UnknownAccountException e) {e.printStackTrace();System.out.println("用户名错误");}catch (IncorrectCredentialsException e){e.printStackTrace();System.out.println("密码错误");}return "login"; // 登录失败跳转到登录页面}
}
  1. 首页代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>$Title$</title>
</head>
<body><h1>首页</h1><hr><ul><li><a href="">用户管理</a></li><li><a href="">商品管理</a></li><li><a href="">菜单管理</a></li><li><a href="">物流管理</a></li></ul>
</body>
</html>
  1. 在ShiroFilter过滤器中放过登录URL
map.put("/user/login","anon");
  1. 页面登录成功后,进入到index.html

6.3 注销

① 配置方式

  1. 在ShiroFilter过滤器中添加注销的URL
map.put("/user/logout","logout");
  1. 在页面直接输入这个URL即可注销

② 代码方式

调用subject的logout方法完成注销,页面访问该Controller的URL

七、授权的基本使用

7.1 校验角色

在自定义的Realm的doGetAuthorizationInfo方法中给当前主体赋予角色

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {// 获取主体中的身份信息String principal = (String) principalCollection.getPrimaryPrincipal();if ("jiuxiao".equals(principal)){ // 给jiuxiao用户赋予admin和user角色SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();// 增加admin和user角色simpleAuthorizationInfo.addRole("admin");simpleAuthorizationInfo.addRole("user");return simpleAuthorizationInfo;}return null;
}

① 编码方式

单个角色:使用subject中的hasRole方法

@Controller
@RequestMapping("/user")
public class UserController {@GetMapping("/save")@ResponseBodypublic String save(){Subject subject = SecurityUtils.getSubject();if (!subject.hasRole("admin")) { // 校验当前角色是否有admin这个角色return "权限不足";}else{return "访问成功";   }}
}

多个角色:使用subject中的hasAllRoles方法【这些角色都有才能访问】

@Controller
@RequestMapping("/user")
public class UserController {@GetMapping("/save")@ResponseBodypublic String save(){List<String> roles = Arrays.asList("admin", "user");if (SecurityUtils.getSubject().hasAllRoles(roles)) {return "访问成功";}else{return "权限不足";}}
}

② 注解方式

单个角色:直接在注解参数中写入对应的角色即可

@Controller
@RequestMapping("/user")
public class UserController {@GetMapping("/save")@ResponseBody@RequiresRoles("admin")public String save(){return "访问成功";}
}

多个角色:在注解中以数组的形式写入多个角色【这些角色都有才能访问】

@GetMapping("/save")
@ResponseBody
@RequiresRoles(value = {"admin","user"})
public String save(){return "访问成功";
}

7.2 校验权限字符串

在自定义的Realm的doGetAuthorizationInfo方法中给当前主体赋予权限字符串

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {String principal = (String) principalCollection.getPrimaryPrincipal();System.out.println("执行授权:"+principal);if ("jiuxiao".equals(principal)){SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();// 添加权限字符串simpleAuthorizationInfo.addStringPermission("user:update");simpleAuthorizationInfo.addStringPermission("product:create");return simpleAuthorizationInfo;}return null;
}

① 编码方式

调用subject中的isPermittedAll方法,参数为可变长参数(可以传一个或者多个)【如果是多个,那么这个主体需要拥有参数里面所有的权限字符串才能访问】

@Controller
@RequestMapping("/user")
public class UserController {@GetMapping("/save")@ResponseBodypublic String save(){Subject subject = SecurityUtils.getSubject();// if (subject.isPermittedAll("user:update")){ // 判断当前主体使用拥有对user资源的001实例的更新操作if (subject.isPermittedAll("user:update","product:update")){return "访问成功";}else{return "权限不足";}}
}

② 注解方式

单个权限字符串:直接在注解参数中写入需要校验的权限字符串即可

@GetMapping("/save")
@ResponseBody
@RequiresPermissions("user:update")
public String save(){return "访问成功";
}

多个权限字符串:在注解中以数组的形式写入多个权限字符串【当前主体主要拥有这些权限字符串才能访问】

@GetMapping("/save")
@ResponseBody
@RequiresPermissions(value = {"user:update","product:create"})
public String save(){return "访问成功";
}

③ 配置方式

在ShiroFilter过滤器使用perms进行权限的校验

map.put("/user/save","perms[user:update,product:delete]"); // 数组中添加权限字符串

如果权限不足,页面抛出401错误

在ShiroFilter中定义权限不足后跳转的URL

注意:定义权限不足跳转URL的方式只限制配置方式,别的方式都不能使用,权限不足时会抛出AuthorizationException异常

八、连接数据库完成授权

8.1 表设计:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for role_perms
-- ----------------------------
DROP TABLE IF EXISTS `role_perms`;
CREATE TABLE `role_perms`  (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',`roleid` int(11) NULL DEFAULT NULL COMMENT '角色id',`permid` int(11) NULL DEFAULT NULL COMMENT '权限id',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_croatian_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Table structure for role_user
-- ----------------------------
DROP TABLE IF EXISTS `role_user`;
CREATE TABLE `role_user`  (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',`roleid` int(11) NULL DEFAULT NULL COMMENT '角色id',`userid` int(11) NULL DEFAULT NULL COMMENT '用户id',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_croatian_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Table structure for t_perms
-- ----------------------------
DROP TABLE IF EXISTS `t_perms`;
CREATE TABLE `t_perms`  (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',`perm` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_croatian_ci NULL DEFAULT NULL COMMENT '权限字符串',`url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_croatian_ci NULL DEFAULT NULL COMMENT '资源URL',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_croatian_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Table structure for t_role
-- ----------------------------
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role`  (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_croatian_ci NULL DEFAULT NULL COMMENT '角色',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_croatian_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user`  (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_croatian_ci NULL DEFAULT NULL COMMENT '用户名(身份信息)',`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_croatian_ci NULL DEFAULT NULL COMMENT '密码(凭证信息)',`salt` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_croatian_ci NULL DEFAULT NULL COMMENT '盐',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_croatian_ci ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;

8.2 POJO

User

@Data
public class User {private Long id;private String username;private String password;private String salt;private List<Role> roles;
}

Role

@Data
public class Role {private Integer id;private String name;private List<Perms> perms;
}

Perms

@Data
public class Perms {private Integer id;private String perm;private String url;
}

8.3 授权角色

  1. Mapper接口与SQL语句
List<Role> selectRoleNameByUserId(String username);
<select id="selectRoleNameByUserId" resultType="com.jiuxiao.pojo.Role">select r.id,r.namefrom t_user uleft join role_user ru on ru.userid = u.idleft join t_role r on r.id = ru.roleidwhere u.username = #{username}
</select>
  1. Service
@Override
public List<Role> getRoleNameByUsername(String username) {return userMapper.selectRoleNameByUsername(username);
}
  1. 自定义Realm中进行授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {String principal = (String) principalCollection.getPrimaryPrincipal();List<Role> roles = userService.getRoleNameByUsername(principal);if(!CollectionUtils.isEmpty(roles)){SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();roles.forEach(role-> simpleAuthorizationInfo.addRole(role.getName()));return simpleAuthorizationInfo;}return null;
}

8.4 授权字符串

  1. Mapper接口与SQL语句
 List<String> selectPermByRoleId(Integer id);
<select id="selectPermByRoleId" resultType="string">select p.permfrom t_role rleft join role_perms rp on r.id = rp.roleidleft join t_perms p on rp.permid = p.idwhere r.id = #{id}
</select>
  1. Service
@Override
public List<String> getPermByRoleId(Integer id) {return userMapper.selectPermByRoleId(id);
}
  1. 自定义Realm进行授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {String principal = (String) principalCollection.getPrimaryPrincipal();List<Role> roles = userService.getRoleNameByUsername(principal);if(!CollectionUtils.isEmpty(roles)){SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();roles.forEach(role-> simpleAuthorizationInfo.addRole(role.getName()));roles.forEach(role->{List<String> perms = userService.getPermByRoleId(role.getId());simpleAuthorizationInfo.addStringPermissions(perms);});return simpleAuthorizationInfo;}return null;
}

九、Shiro与thymeleaf整合

9.1 导入依赖

<dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.0.0</version>
</dependency>

9.2 配置方言

@Bean
public ShiroDialect shiroDialect(){return new ShiroDialect();
}

9.3 引入工作空间

<html lang="en" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

9.4 常用标签使用

<!-- 验证当前用户是否为"访客",即未认证的用户 -->
<p shiro:guest="">未认证</p><!-- 认证通过或者已经"记住我"的用户 -->
<p shiro:user="">hello</p><!-- 认证通过的用户 -->
<p shiro:authenticated="">hello</p><!-- 输出当前用户信息,通常为账号登录信息 -->
<p shiro:principal></p><!-- 判断当前用户是否拥有该角色 -->
<p shiro:hasRole="admin">拥有该角色</p><!-- 当前用户没有该角色认证通过 -->
<p shiro:lacksRole="user">没有改角色</p><!-- 判断当前用户是否拥有以下所有角色 -->
<p shiro:hasAllRoles="admin,user"></p><!-- 判断当前用户是否拥有以下任意一个角色 -->
<p shiro:hasAnyRoles="admin,user"></p><!-- 判断当前用户是否拥有以下权限字符串 -->
<p shiro:hasPermission="user:add"></p><!-- 当前用户没有该权限字符串认证通过 -->
<p shiro:lacksPermission="user:add"></p>

相关文章:

Shiro安全框架简介

一、权限管理 1.1 什么是权限管理 基本上只要涉及到用户参数的系统都要进行权限管理&#xff0c;使用权限管理实现了对用户访问系统的控制&#xff0c;不同的用户访问不同的资源。按照安全规则或者安全策略控制用户访问资源&#xff0c;而且只能访问被授权的资源 权限管理包括认…...

三行Python代码,让数据处理速度提高2到6倍

本文可以教你仅使用 3 行代码&#xff0c;大大加快数据预处理的速度。 Python 是机器学习领域内的首选编程语言&#xff0c;它易于使用&#xff0c;也有很多出色的库来帮助你更快处理数据。但当我们面临大量数据时&#xff0c;一些问题就会显现…… 在默认情况下&#xff0c;…...

空间向量模长

// 空间向量模长 #include <stdio.h> #include <stdlib.h> #include <math.h> int main(int argc, char **argv) { float x, y, z; float mochang 0.0; x y z 0.0; if (argc ! 4) { printf("usage:%s x y z\n", argv[1]); …...

活动需求中灵活使用Redis提升生产力

抽奖 一堆用户参与进来&#xff0c;然后随机抽取几个幸运用户给予实物/虚拟的奖品&#xff1b;此时&#xff0c;开发人员就需要写上一个抽奖的算法&#xff0c;来实现幸运用户的抽取&#xff1b;其实我们完全可以利用Redis的集合&#xff08;Set&#xff09;&#xff0c;就能轻…...

Java知识点学习(第16天)

Innodb是如何实现事务的&#xff1f; innodb通过Buffer Pool&#xff0c;LogBuffer&#xff0c;Redo Log&#xff0c;Undo Log来实现事务&#xff0c;以一个update语句为例&#xff1a; innodb在收到一个update语句后&#xff0c;会先根据条件找到数据所在的页&#xff0c;并…...

ORA-1688: unable to extend table AUDSYS.AUD$UNIFIED

昨晚正在外滩玩&#xff0c;有个客户发过来一段报错&#xff0c;已经影响到业务了。一看就是12C以后版本才有的问题&#xff0c;&#xff0c;赶紧在手机中收到临时解决办法 报错如下 ORA-1688: unable to extend table AUDSYS.AUD$UNIFIED partition SYS_P42549 by 1024 in t…...

抖音滑块以及轨迹分析

声明 本文以教学为基准、本文提供的可操作性不得用于任何商业用途和违法违规场景。 本人对任何原因在使用本人中提供的代码和策略时可能对用户自己或他人造成的任何形式的损失和伤害不承担责任。 如有侵权,请联系我进行删除。 我们在web端打开用户主页的时候,时不时的会出现滑…...

C#生成单色bmp图片,转为单色bmp图片 任意语言完全用字节拼一张单色图,LCD取模 其它格式图片转为单色图

最终效果&#xff1a; V1.8.2 20230419 文字生成单色BMP图片4.exe 默认1280*720 如果显示不全&#xff0c;请把宽和高加大 字体加大。 首先&#xff0c;用windows画板生成一张1*1白色单色图作为标准&#xff0c;数据如下&#xff1a; 数据解析参考&#xff1a;BMP图像文件完…...

【瑞吉外卖】002 -- 后台登录功能开发

本文章为对 黑马程序员Java项目实战《瑞吉外卖》的学习记录 目录 一、需求分析 1、页面原型展示 2、登录页面展示 3、查看登录请求信息 4、数据模型 二、代码开发 1、创建实体类Employee&#xff0c;和employee表进行映射 2、创建包结构&#xff1a;&#xff08;Controller、Se…...

【电动汽车充电站有序充电调度的分散式优化】基于蒙特卡诺和拉格朗日的电动汽车优化调度(分时电价调度)(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

java IO流_1

目录 分类 字节流 InputStream OutputStream 文件拷贝 字符流 FileReader FileWriter 处理流 BufferedReader BufferedWriter 文本拷贝 流是从起源到接受的有序数据&#xff0c;通过流的方式允许程序使用相同的方式来访问不同的输入/输出源。 分类 按数据…...

【回忆 总结】我的大学四年

大学四年关键词速览 如果穿越回大一&#xff0c;你想对大一的你提什么最重要的建议&#xff1f;同样是上网课&#xff0c;我为何能比大多数同学学的更好&#xff1f;回到学校&#xff0c;我的大二似乎一帆风顺&#xff1f;在不断的迷茫和徘徊中&#xff0c;大三的我做出的决定&…...

深度解析OEKO

【深度解析OEKO】 什么是OEKO-TEX Standard 100&#xff1f; OEKO-TEX Standard 100现在是使用最为广泛的纺织品生态标志。OEKO-TEX Standard 100规定的标准是根据最新的科学知识&#xff0c;对纱线、纤维以及各类纺织品的有害物质含量规定限度。只有按照严格检测和检查程序提供…...

Golang gorm

GORM 指南 | GORM - The fantastic ORM library for Golang, aims to be developer friendly. 一 对多入门 比如要开发cmdb的系统&#xff0c;无论是硬件还是软件。硬件对应的就是对应的哪个开发在用。或者服务对应的是哪个业务模块在使用&#xff0c;或者应用谁在使用。那么这…...

rk3568 适配摄像头 (CIF协议)

rk3568 适配摄像头 (CIF协议) 在RK3568处理器中&#xff0c;支持CIF协议的摄像头可以通过CSI接口连接到处理器&#xff0c;实现视频数据的采集和处理。同时&#xff0c;RK3568还支持多种图像处理算法和编解码器&#xff0c;可以对采集到的视频数据进行实时处理和压缩&#xff…...

今天面试招了个25K的测试员,从腾讯出来的果然都有两把刷子···

公司前段时间缺人&#xff0c;也面了不少测试&#xff0c;前面一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资在15-25k&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。看简历很多都是4年工作经验&#xff0c;但面试中&#xff0c;不…...

Redis---集群环境准备

一、redis集群环境准备 1、部署Redis集群的目的&#xff1a; 多台服务器一起提供数据存储服务&#xff1b; 实现数据的分布式存储&#xff1b; 可以实现服务的高可用&#xff1b; 可用实现数据自动备份&#xff1b; 2、服务器IP地址及端口&#xff1a; 主机名 IP地…...

数据结构考研版——队列的配置问题

一、正常配置下的情况 队空状态 frontrear;入队操作 出队操作 队满状态 在正常配置下元素的个数&#xff08;rear>front&#xff09; 当rear<front 综上所述用一个表达式表示&#xff1a;(rear-frontmaxSize)%maxSize 二、非正常配置下的情况1 队空状态 入队操作…...

【SOAP-WebService系列】SOAP学习笔记

目录 1、SOAP是什么&#xff1f; 2、SOAP特性 3、SOAP消息组成 4、SOAP调用 5、SOAP和HTTP 1、SOAP是什么&#xff1f; SOAP(Simple Object Access Protocol&#xff0c;即简单对象访问协议) &#xff0c;是一个轻量级协议&#xff0c;用于在分散的分布式环境中使用XML在对…...

材料科学|名词解释终版!!!

晶体&#xff1a;组成物质的原子&#xff0c;分子或离子按照一定的周期性规则排列形成的固体。 非晶体&#xff1a;原子在三维空间的不规则排列&#xff0c;长程无序&#xff0c;各向同性。 晶体结构&#xff1a;原子&#xff0c;离子&#xff0c;原子团按照空间点阵而进行的…...

永久免费内网穿透不限制速度

市面上的免费内网穿透大都有格式各样的限制&#xff0c;什么限制流量啊&#xff0c;每个月要签到打卡啊&#xff0c;还有更改域名地址等&#xff0c;只有神卓互联内网穿透是永久免费没有限制的&#xff0c;白嫖也可以。 这篇文章分享了3个方案&#xff0c;按照性能和综合指标排…...

JAVA开发运维(云基础设备监控)

在大型的商用系统中&#xff0c;经常需要监控云设备的健康状态&#xff0c;性能情况&#xff0c;流量数据等。及时发现系统问题&#xff0c;及时修复&#xff0c;以确保系统的高可用。检查云资源的工作内容主要包括基础监控、主动拨测、用户体验、APM监控、指标体系、业务分析、…...

现在备考2023年5月软考网络工程师时间够吗?

距离2023年5月软考还有1个多月的时间&#xff0c;备考网络工程师的时间是够的&#xff0c;以下是一些备考方法&#xff1a; 1.了解考试内容 在你开始学习考试之前&#xff0c;了解考试的形式和内容是很重要的。这将帮助你把注意力集中在最有可能被测试的领域。你应该复习考试…...

webp怎么转换成png,4个方法教你快速处理

webp怎么转换成png&#xff1f;目前在一些比较大的图片素材网站下载的图片都是webp格式的。我们都知道webp格式图片&#xff0c;它在正常的图片浏览器中是无法打开的。 所以说我们要把webp图片转变成png格式&#xff0c;正常来说我们常用的图片处理软件也能进行格式转换&#x…...

程序员能干多久?程序员能干到多大年龄?

程序员可以工作多少年?大多数程序员认为程序员是吃青春饭的工作。编程只能干到30岁&#xff0c;最长可达35岁。我经常听到这样的话&#xff0c;都让人倍感压力。今天&#xff0c;我们来谈谈这个老话题...... 程序员能干多久&#xff1f; 根据国外的经验来说&#xff0c;干到…...

采购系统是如何管理供应商的?

随着数字化的推进&#xff0c;企业面临着越来越多的供应商管理问题。企业采购数字化转型已经成为大势所趋&#xff0c;对于采购数字化转型而言&#xff0c;供应商管理是重要一环。 供应商准入管理 在供应商准入阶段&#xff0c;企业需要从供应商资质、财务能力、信誉能力、管理…...

Linux学习笔记(2)--一些内核接口

1&#xff09;dump_stack dump_stack()是Linux内核中的一个函数&#xff0c;用于在内核中输出当前的函数调用栈。该函数会输出当前线程&#xff08;或进程&#xff09;的函数调用栈信息&#xff0c;以及相应的调用地址和虚拟内存地址等信息&#xff0c;一般用于诊断程序运行时…...

学习风`宇博客用户权限菜单模块

文章目录 用户-角色-菜单-资源 各表关系图菜单 和 路由菜单表及分析分析 /api/admin/user/menus接口MenuServiceImpl#listUserMenus接口返回示例及分析 前端代码分析menu.jsSideBar.vue 接口权限控制资源表 及 分析分析 WebSecurityConfig权限控制整体流程先说登录UserDetailsS…...

centos7.6部署ELK集群(一)之elasticsearch7.7.0集群部署

32.3. 部署es7.7.0 32.3.1. 下载es&#xff08;各节点都做&#xff09; wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.7.0-linux-x86_64.tar.gz 32.3.2. 解压至安装目录&#xff08;各节点都做&#xff09; tar -xvf elasticsearch-7.7.0-li…...

leetcode142. 环形链表 II

给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数…...