Shiro安全服务
Shiro#
为什么要⽤shiro:
1.项⽬中的密码是否可以明⽂存储?
2.是否任意访客,⽆论是否登录都可以访问任何功能?
3.项⽬中的各种功能操作,是否是所有⽤户都可以随意使⽤?
综上,当项⽬中的某些功能被使⽤时,需要进⾏安全校验,进⽽保证整个系统的运⾏秩序。
Shiro是什么#
- Apache Shiro 是 Java 的⼀个安全(权限)框架。
Shiro 可以轻松的完成:身份认证、授权、加密、会话管理等功能 - Shiro 可以⾮常容易的开发出⾜够好的应⽤,其不仅可以⽤在JavaSE 环境,也可以⽤在 JavaEE 环境。
功能强⼤且易⽤,可以快速轻松地保护任何应⽤程序 ( 从最⼩的移动应⽤程序到最⼤的Web和企业应⽤程序。) - ⽅便的与Web 集成和搭建缓存。
- 下载:http://shiro.apache.org/
功能简介#
基本功能点如下图所示:
- Authentication:身份认证/登录,验证⽤户是不是拥有相应的身份;
- Authorization:授权,即权限验证,验证某个已认证的⽤户是否拥有某个权限;即判断⽤户是否能进⾏什么操作。如:验证某个⽤户是否拥有某个⻆⾊。或者细粒度的验证某个⽤户对某个资源是否具有某个权限;
- Session Manager:会话管理,即⽤户登录后就是⼀次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境,也可以是 Web 环境的;
- Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,⽽不是明⽂存储;
- Web Support:Web ⽀持,可以⾮常容易的集成到Web 环境;
- Caching:缓存,⽐如⽤户登录后,其⽤户信息、拥有的⻆⾊/权限不必每次去查,这样可以提⾼效率;
- Remember Me:记住我,这个是⾮常常⻅的功能,即⼀次登录后,下次再来的话可以⽴即知道你是哪个⽤户
High-Level Overview 高级概述#
在概念层,Shiro 架构包含三个主要的理念:Subject,SecurityManager和 Realm。下面的图展示了这些组件如何相互作用,我们将在下面依次对其进行描述。
- Subject:当前用户,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它–当前和软件交互的任何事件。
- SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞。
- Realms:用于进行权限信息的验证,我们自己实现。Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。
我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
Shiro 认证过程#
整合SpringBoot#
依赖#
<dependencies>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- shiro-spring 整合包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
</dependencies>
登录页面控制#
package com.example.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
//判断登录是否成功
@RequestMapping("/login")
public String index4(String username, String pwd, Model model){
//获取当前用户
Subject currentUser = SecurityUtils.getSubject();
//将前端的账号和密码封装成密码令牌
UsernamePasswordToken token = new UsernamePasswordToken(username,pwd);
//登录验证
// let's login the current user so we can check against roles and permissions:
try {
//自动判断登录 ,调用 realm对象的doGetAuthenticationInfo方法
currentUser.login(token);
return "index";
} catch (UnknownAccountException uae) {
model.addAttribute("msg","账号错误");
return "login";
} catch (IncorrectCredentialsException ice) {
model.addAttribute("msg","密码错误");
return "login";
}
}
}
设置shrio配置文件#
package com.example.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
//三大对象 ShiroFilterFactoryBean,过滤器
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultSecurityManager defaultSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultSecurityManager);
Map<String, String> map = new LinkedHashMap<>();
//设置权限访问
map.put("/user/add","perms[user:add]");
map.put("/user/update","perms[user:update]");
map.put("/user/add1","perms[user:add1]");
//设置登录拦截
//shiro的内置过滤器
/*
anon:不需要认证就可以访问
authc:需要认证
user:必须要 记住我 功能
perms:拥有对某个资源的权限才能访问
role:拥有对某个角色权限才能访问
*/
//该行代码必须放在授权的代码下,否则授权的拦截无效!
map.put("/user/**","authc");
//设置登录url
bean.setLoginUrl("/toLogin");
//设置未授权页面
bean.setUnauthorizedUrl("/noperms");
//设置过滤链map
bean.setFilterChainDefinitionMap(map);
return bean;
}
//DefaultSecurityManager对象
@Bean
public DefaultSecurityManager DefaultSecurityManager(UserShrio userShrio){
DefaultSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(userShrio);
return securityManager;
}
//realm对象
@Bean
public UserShrio userShrio(){
return new UserShrio();
}
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
配置shiro 认证和权限拦截#
登录拦截(用户认证)#
- 先调用
currentUser.login(token); //启动自动登录判断
- 根据shrio配置文件ShiroConfig中的过滤器配置的 d登录 的拦截
- 去调用shiro对象的doGetAuthenticationInfo
权限认证#
-
根据shrio配置文件ShiroConfig中的过滤器配置的 权限授权 的拦截
-
去调用shiro对象的doGetAuthorizationInfo
shiro真正拦截的地方#
package com.example.config;
import com.example.pojo.User;
import com.example.service.UserService;
import org.apache.shiro.SecurityUtils;
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.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
public class UserShrio extends AuthorizingRealm {
@Autowired
private UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("授权---doGetAuthorizationInfo");
//拿到当前对象
Subject subject = SecurityUtils.getSubject();
//取出在认证传递的User对象
User user = (User) subject.getPrincipal();
System.out.println("认证的对象---------"+user);
//给用户添加上相应的权限(来自数据库中的perms字段)
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission(user.getPerm());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("认证---doGetAuthenticationInfo");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//从数据库查询账号密码
User user = userService.queryByUserName(token.getUsername());
//如果密码不正确
if (user==null){
//会自定抛出错误UnknownAccountException
return null;
}
//SimpleAuthenticationInfo:(参数)三个 或者四个
/*
用户名–此处传的是用户对象 直接通过token.getPrincipal()方法获取—获取当前记录的用户名,从这个用户名获取一系列的对应需求属性。
这里传入user 在上面授权中就可以使用
userInfo.getPassword(), //密码—从数据库中获取的密码
salt, 盐–用于加密密码对比
getName() //当前的realm名
*/
//userInfo, //
//密码认证 ,shiro自己做
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPwd(),"");
return info;
}
}
整合thymeleaf#
依赖#
<!-- thymeleaf-extras-shiro -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
使用#
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>index</h1>
<!--//根据不同的权限判断是否显示-->
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a>
</div>
<br>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">update</a> <br>
</div>
<br>
<a th:href="@{/user/add1}">add</a>
</body>
</html>
作者:Esofar
出处:https://www.cnblogs.com/firsthelloworld/p/13554535.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本