365bet中文版

深入理解Shiro

📅 2026-01-15 22:57:00 👤 admin 👁️ 7434 🏷️ 274

ShiroShiro介绍​ Apache Shiro(发音为“shee-roh”,日语中“城堡”的意思)是一个功能强大且易于使用的 Java 安全框架,可执行身份验证、授权、加密和会话管理,可用于保护任何应用程序 -从命令行应用程序、移动应用程序到最大的 Web 和企业应用程序。

Shiro 提供应用程序安全 API 来执行以下方面(我喜欢将它们称为应用程序安全的 4 个基石):

身份验证 - 证明用户身份,通常称为用户“登录”。授权-访问控制密码学 - 保护或隐藏数据免遭窥探会话管理 - 每个用户的时间敏感状态Shiro 还支持一些辅助功能,例如 Web 应用程序安全性、单元测试和多线程支持,但这些功能的存在是为了加强上述四个主要问题。

官网QuickStartTutorial.java代码语言:javascript复制import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.*;

import org.apache.shiro.config.IniSecurityManagerFactory;

import org.apache.shiro.mgt.SecurityManager;

import org.apache.shiro.session.Session;

import org.apache.shiro.subject.Subject;

import org.apache.shiro.util.Factory;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

public class Tutorial {

private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class);

public static void main(String[] args) {

log.info("My First Apache Shiro Application");

Factory factory = new IniSecurityManagerFactory("classpath:shiro.ini");

SecurityManager securityManager = factory.getInstance();

SecurityUtils.setSecurityManager(securityManager);

//1. 获取当前的用户对象Subject

Subject currentUser = SecurityUtils.getSubject();

// Do some stuff with a Session (no need for a web or EJB container!!!)

//2. 通过当前用户拿到session

Session session = currentUser.getSession();

session.setAttribute("someKey", "aValue");

String value = (String) session.getAttribute("someKey");

if (value.equals("aValue")) {

log.info("Retrieved the correct value! [" + value + "]");

}

// let's login the current user so we can check against roles and permissions:

//3. 判断当前的用户是被认证

if (!currentUser.isAuthenticated()) {

UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");

token.setRememberMe(true); //设置记住我

try {

currentUser.login(token); //执行了登录操作(暂时看不到 )

} catch (UnknownAccountException uae) {

//未知的账户

log.info("There is no user with username of " + token.getPrincipal());

} catch (IncorrectCredentialsException ice) {

//

log.info("Password for account " + token.getPrincipal() + " was incorrect!");

} catch (LockedAccountException lae) {

log.info("The account for username " + token.getPrincipal() + " is locked. " +

"Please contact your administrator to unlock it.");

}

// ... catch more exceptions here (maybe custom ones specific to your application?

catch (AuthenticationException ae) {

//unexpected condition? error?

}

}

//say who they are:

//print their identifying principal (in this case, a username):

log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

//test a role:

//4. 测试当前用户是否有角色

if (currentUser.hasRole("schwartz")) {

log.info("May the Schwartz be with you!");

} else {

log.info("Hello, mere mortal.");

}

//test a typed permission (not instance-level)

//粗粒度 (暂时为止)

if (currentUser.isPermitted("lightsaber:wield")) {

log.info("You may use a lightsaber ring. Use it wisely.");

} else {

log.info("Sorry, lightsaber rings are for schwartz masters only.");

}

//细粒度

/**

* 这些权限都是在shiro.ini中的

* admin = *

* schwartz = lightsaber:*

* goodguy = winnebago:drive:eagle5

*/

//是否拥有更高的权限

if (currentUser.isPermitted("winnebago:drive:eagle5")) {

log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +

"Here are the keys - have fun!");

} else {

log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");

}

//all done - log out!注销

currentUser.logout();

//结束

System.exit(0);

}

}我们当前使用的shiro很多的用法都可以从QuickStart中了解到

shiro.ini代码语言:javascript复制[users]

root = secret, admin

guest = guest, guest

presidentskroob = 12345, president

darkhelmet = ludicrousspeed, darklord, schwartz

lonestarr = vespa, goodguy, schwartz

# -----------------------------------------------------------------------------

# Roles with assigned permissions

# roleName = perm1, perm2, ..., permN

# -----------------------------------------------------------------------------

[roles]

admin = *

schwartz = lightsaber:*

goodguy = winnebago:drive:eagle5实现导入依赖代码语言:javascript复制

org.apache.shiro

shiro-spring

1.4.1

org.apache.shiro

shiro-core

1.4.1

com.github.theborakompanioni

thymeleaf-extras-shiro

2.1.0

org.springframework.boot

spring-boot-starter-thymeleaf

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-test

test

org.projectlombok

lombok

true

com.mysql

mysql-connector-j

8.0.32

org.mybatis.spring.boot

mybatis-spring-boot-starter

2.1.2

log4j

log4j

1.2.17

项目实现背景介绍代码语言:javascript复制 我们日常使用的所有应用程序都存在着很多不安全的问题,为了解决这些问题,我们学习SpringSecurity、shiro等技术,为实现密码安全问题,我们会使用md5,md5盐值加密....一系列操作​ 在应用程序中,为了更好的盈利,我们会将普通用户和会员用户进行区分,同时,对于两者之间展现的页面,功能也会有所不同。那么如何实现这种不同

​ 本次练习项目就会通过shiro来实现这些操作

功能实现介绍有 vip1身份的用户会展示有关vip1的界面,以及普通用户界面有 vip2身份的用户会展示有关vip1的界面,以及普通用户界面普通用户只会展示普通用户界面登录、退出提示用户名或者密码错误….数据库信息展示

image未登录界面展示

登录及其错误提示

vip1用户所在页面展示

普通用户页面展示

关键代码详解首先,我们导入以来完成后,需要进行配置自定义配置shiro类,同时用@Configuration注解标注

代码语言:javascript复制@Configuration

//自定义配置类,实现shiro的配置

public class shiroConfig{

//1. shiroFilterFactoryBean

//2. DefaultWebSecurityManager 安全管理器(关联Realm)

//3. 创建realm对象

}在shiro配置类中有三个内置类是十分重要的,他们分别是

ShiroFilterFactoryBeanDefaultWebSecurityManagerrealm对象他们决定了shiro的工作机制及流程

如图所示

Realm :​ Realm : 作为接收需要接受的安全数据的对象,起着与我们底层的数据交互的作用,它通过封装安全数据,然后放入在Spring的容器中

代码语言:javascript复制//3. 创建realm对象,需要自定义类,然后交给spring托管(放到bean中)

@Bean(name = "userRealm")

public UserRealm userRealm(){

return new UserRealm();

}DefaultWebSecurityManager :​ DefaultWebSecurityManager : 作为安全管理员,他的作用就是来关联我们放置在容器中Realm对象,同时,将自身再封装成为Bean交给spring容器来托管,然后等待ShiroFilterFactoryBean的调用 。可以看出他是shiro的核心,相当于我们springMVC中的DispatcherServlet

代码语言:javascript复制//2. DefaultWebSecurityManager 安全管理器(关联Realm)

//@Qualifier("userRealm")中的内容就是下面第三步中的@Bean中name属性

@Bean(name = "securityManager")

public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){

DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

//用securityManager来关联realm

securityManager.setRealm(userRealm);

//因为我们第三步已经将userRealm交给了spring接管,所以需要传参数来得到,而不是直接new

return securityManager;

}ShiroFilterFactoryBean :​ **ShiroFilterFactoryBean : 作用就是继续关联安全管理器DefaultWebSecurityManager,然后同时将自己也封装成bean **

代码语言:javascript复制//1. shiroFilterFactoryBean

@Bean

public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){

ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();

//关联DefaultWebSecurityManager安全管理器

bean.setSecurityManager(defaultWebSecurityManager);

/**

* 添加内置过滤器

* ● anon : 无需认证即可访问

* ● authc :必须认证才能访问

* ● user :必须拥有记住我才能访问

* ● perms : 拥有对某个资源的权限才能访问

* ● role : 拥有某个角色权限才能访问

*/

//拦截的请求-----------------------------------

Map filterMap = new LinkedHashMap<>();

filterMap.put("/vipFirst","perms[vip1]");

filterMap.put("/vipSecond","perms[vip2]");

bean.setFilterChainDefinitionMap(filterMap);//他的参数是从map集合中拿的,所以需要提前设置一个集合

//如果没有权限,设置跳转页面(登录的请求)

bean.setLoginUrl("/login");

//未授权的请求

//bean.setUnauthorizedUrl("");

//-------------------------------------------

return bean;

}

他的拦截请求会通过一个map集合来操作,但是shiro也存在很多的内置过滤器 , 通过这些过滤器,我们就可以实现请求过滤操作

代码语言:javascript复制 * ● anon : 无需认证即可访问

* ● authc :必须认证才能访问

* ● user :必须拥有记住我才能访问

* ● perms : 拥有对某个资源的权限才能访问

* ● role : 拥有某个角色权限才能访问​ 在ShiroFilterFactoryBean中我们可以做很多需要的操作比如:

请求的拦截未授权用户页面的跳转….上述三者的执行顺序虽然是从:S - > D - > R (简写)

但是我们在按逻辑写的时候确实 从 : R- > D -> S

对外核心Subject与应用程序的代码直接进行交互的对象就是Subject, 它代表的是当前用户,这个用户不仅仅值得是一个具体的人,而是与当前应用交互的任何事物。与Subject进行交互,他就会将所有的东西全都委托给我们的安全管理员(DefaultWebSecurityManager ),他才是真正的执行者。

对内核心Realm为什么这里我将他作为对内核心,因为我们所有需要进行安全操作的事情都在他的实现类中完成

代码语言:javascript复制//3. 创建realm对象

//实现的两个方法就是 springsecurity中授权和认证

public class UserRealm extends AuthorizingRealm {

//认证

// ....

//授权

}比如:

对用户进行授权认证密码保护…认证

代码语言:javascript复制//认证

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

System.out.println("===> 执行了认证方法");

//获取当前的令牌,及其其中的信息

UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;

// 获取当前的用户 , 封装用户的登录数据(在controller的登录方法中实现)

User user = userService.selectUser(userToken.getUsername());

//判断是否与数据库中的相同,如果不相同

if (!user.getUsername().equals(userToken.getUsername())){

return null; //就会抛出异常

}

Subject subject = SecurityUtils.getSubject();

Session session = subject.getSession();

session.setAttribute("loginUser",user);

//密码认证, shrio来帮助我们实现

AuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),user.getDept());

return info;

}授权

代码语言:javascript复制//授权

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

System.out.println("===> 执行了授权方法");

//执行授权的功能

SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

//将用户所具有的权限存放在数据库中,然后登录时获取用户的全部信息

Subject subject = SecurityUtils.getSubject();

//在用户进行认证时我们进行这个操作【AuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),user.getDept());】

//通过他我们就可以拿到数据

User user = (User) subject.getPrincipal();

//设置当前用户的权限

authorizationInfo.addStringPermission(user.getDept());

//authorizationInfo.addRole("vip1");

return authorizationInfo;

}

相关推荐

vnr教程 vnr打不开

为什么我打开VNR失败? - 解决方法分享 您是否曾经遇到过按下VNR的启动按钮,却没有反应,或者试图打开VNR时出现错误提示消息的情况?在使用

网络游戏排行榜

新浪简介 | About Sina | 网站地图 | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 通行证注册 | 产品答疑 Copyright © 1996-2023 SINA Corporation,

清心火的中成药有哪些

清心火的中成药有哪些刘海丽小荷医典专家团副主任医师|辽宁省中医药大学附属第四医院 脑病科三甲清心火的中成药主要有牛黄清心火、导赤