Shiro之认证

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
Shiro之认证


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 系列文章目录
  • 前言
  • 一、什么是Shiro
  • 二、Shiro的核心功能
  • 三、Shiro的核心组件
  • 四、Shiro之认证
    • 项目搭建
    • 自定义Realm
    • 容器管理shiro对象
    • 多Realm认证
    • 多Realm认证策略
    • 异常处理
    • 使用散列算法加密认证
    • 过滤器
    • 获取认证数据
    • Shiro会话
    • 会话管理器
    • 退出登录
    • 记住我
  • 总结


前言

提示:这里可以添加本文要记录的大概内容:

当涉及到应用程序的安全性时,身份验证和授权是至关重要的环节。Shiro 框架以其简单而强大的特性,成为了处理这两个关键任务的流行选择。在这篇博客中,我们将深入探讨 Shiro 框架的认证功能。
通过使用 Shiro,你将能够轻松地管理用户的身份验证过程,确保只有经过授权的用户能够访问受保护的资源。我们将了解 Shiro 的核心概念,如 Subject、Realm 和 Credentials,以及它们在认证过程中的作用。
博客将详细介绍 Shiro 支持的多种认证方式,包括用户名和密码、证书、RememberMe 等。你将学会如何配置和实现这些认证方式,以满足你的应用程序的特定需求。
我们还将探讨 Shiro 的一些高级特性,如权限管理、会话管理和加密等。这些特性将帮助你进一步加强应用程序的安全性,并提供更细粒度的访问控制。
无论你是刚刚开始使用 Shiro,还是已经有一定经验的开发者,这篇博客都将为你提供宝贵的资源和深入的理解。让我们一起探索 Shiro 的认证功能,为你的应用程序构建一个安全可靠的基础。


提示:以下是本篇文章正文内容,下面案例可供参考

一、什么是Shiro

Shiro是apache旗下的一个开源安全框架,它可以帮助我们完成身份认证,授权、加密、会话管理等功能。Shiro 的主要目标是让开发者能够轻松地保护他们的应用程序,使其免受未经授权的访问。它有如下特点:

  • 不依赖任何的框架或者容器捆绑,可以独立运行
  • 内置会话管理,适用于Web以及非Web的环境
  • 支持缓存,以提升应用程序的性能
  • 易于理解的API
  • 简单的身份认证,支持多种数据源
  • 简单的认证与授权

二、Shiro的核心功能

Shiro 是一个强大而灵活的 Java 安全框架,其核心功能包括身份验证、授权、会话管理和加密等。以下是 Shiro 的一些核心功能的详细介绍:

  • 身份验证(Authentication):Shiro 支持多种身份验证方式,如用户名和密码、证书、RememberMe 等。它可以与各种数据源集成,如关系型数据库、LDAP 等,以验证用户的身份。
  • 授权(Authorization):一旦用户通过身份验证,Shiro 可以根据用户的角色和权限进行授权。它支持基于角色的访问控制(RBAC)和基于权限的访问控制(PBAC)等授权模型。
  • 会话管理(Session Management):Shiro 提供了会话管理功能,包括创建、维护和销毁会话。它可以与 Web 容器集成,以实现会话的超时和续命。
  • 加密(Encryption):Shiro 支持密码加密、数据加密和签名等加密功能,以确保敏感数据的安全性。
  • 缓存(Caching):Shiro 内置了缓存机制,可以提高性能并减少对数据源的访问。
  • 领域对象(Realm):Shiro 鼓励使用领域对象来表示安全主体(如用户、角色等)和权限,使代码更加清晰和易于维护。
  • 灵活性(Flexibility):Shiro 具有高度的灵活性,可以轻松地与其他框架(如 Spring、Hibernate 等)集成。
  • 可配置性(Configurability):Shiro 提供了灵活的配置选项,可以通过配置文件或注解来定义安全策略。

三、Shiro的核心组件

Shiro 框架有许多核心组件,它们协同工作以提供全面的安全功能。以下是一些 Shiro 的核心组件:

  • Subject:Subject 表示当前执行操作的用户或主体。它持有用户的身份信息和权限信息,并提供了执行安全操作的接口。
  • SecurityManager:SecurityManager 是 Shiro 框架的核心,它负责管理所有的安全操作。它是整个 Shiro 安全体系的入口点,应用程序通过调用 SecurityManager 来进行身份验证、授权等操作。
  • Realm:Realm 是 Shiro 与数据源进行交互的桥梁。它用于存储和检索用户、角色和权限等信息。Shiro 支持多种类型的 Realm,如基于内存的 Realm、基于数据库的 Realm 等。
  • Session:Session 用于管理用户与应用程序之间的会话。它跟踪用户的登录状态,并在需要时提供对用户信息的访问。
  • Authenticator:Authenticator 负责执行用户的身份验证。它可以通过用户名和密码、证书等方式验证用户的身份。
  • Authorizer:Authorizer 负责执行授权操作,即确定用户是否具有访问特定资源或执行特定操作的权限。
  • CachingManager:CachingManager 用于管理 Shiro 的缓存。它可以缓存用户、角色和权限等信息,以提高性能。
  • LogoutFilter:LogoutFilter 用于处理用户的登出操作。它确保在用户登出后清除相关的会话信息。

四、Shiro之认证

项目搭建

1.准备名为myshiro的mysql数据库
2.创建SpringBoot项目,加入相关依赖

<dependencies><!-- SpringMVC --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Thymeleaf --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!-- Mysql驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- MyBatisPlus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.0</version></dependency><!-- shiro和spring整合包 --><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.9.0</version></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- Junit --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- Spring-jdbc --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency>
</dependencies>

3.编写配置文件application.yml

server:port: 80#日志格式
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'# 数据源
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql:///myshiro?serverTimezone=UTCusername: rootpassword: root

4.在template文件夹编写项目主页面main.html

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>主页面</title>
</head>
<body>
<h1>主页面</h1>
</body>
</html>

5.编写页面跳转控制器

@Controller
public class PageController {@RequestMapping("/{page}")public String showPage(@PathVariable String page) {return page;}// 忽略favicon.ico的获取@GetMapping("favicon.ico")@ResponseBodypublic void noFavicon() {}
}

自定义Realm

Realm 可以与数据源(如关系型数据库、LDAP 服务器等)进行集成,以获取和验证用户的认证和授权信息。Shiro 提供了多种内置的 Realm 实现,如 IniRealm、JdbcRealm、LdapRealm 等,同时也支持自定义 Realm 以满足特定的需求。
在 Shiro 的安全体系中,Subject 是代表当前用户的对象,它与 Realm 进行交互以进行认证和授权操作。当用户尝试进行身份验证或请求授权时,Subject 会将请求转发给配置的 Realm,Realm 则根据存储的用户信息进行验证和授权决策。

1.准备数据表

CREATE TABLE `users` (`uid` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,PRIMARY KEY (`uid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;INSERT INTO `users` VALUES (1, 'zhangsan', '123');

2.编写实体类

@Data
public class Users {private Integer uid;private String username;private String password;
}

3.编写mapper接口

@Mapper
public interface UsersMapper extends BaseMapper<Users> {
}

4.在启动类加载mapper接口

@SpringBootApplication
@MapperScan("com.zhangsan.myshiro1.mapper")
public class Myshiro1Application {public static void main(String[] args) {SpringApplication.run(Myshiro1Application.class, args);}
}

5.编写自定义Realm类

public class MyRealm extends AuthorizingRealm {@Autowiredprivate UsersMapper usersMapper;// 自定义认证方法@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {// 1.获取用户输入的用户名UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();// 2.根据用户名查询用户QueryWrapper<Users> wrapper = new QueryWrapper<Users>().eq("username", username);Users users = usersMapper.selectOne(wrapper);// 3.将查询到的用户封装为认证信息if (users == null) {throw new UnknownAccountException("账户不存在");}/*** 参数1:用户* 参数2:密码* 参数3:Realm名*/return new SimpleAuthenticationInfo(users,users.getPassword(),"myRealm");}// 自定义授权方法@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}
}

容器管理shiro对象

将自定义Realm放入SecurityManager对象中

@Configuration
public class ShiroConfig {// 自定义Realm@Beanpublic MyRealm myRealm(){return new MyRealm();}// SecurityManager对象@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){DefaultWebSecurityManager defaultSecurityManager=new DefaultWebSecurityManager();// 自定义Realm放入SecurityManager中defaultSecurityManager.setRealm(myRealm);return defaultSecurityManager;}
}

2.创建UserService对象

@Service
public class UsersService {@Autowiredprivate DefaultWebSecurityManager securityManager;public void userLogin(String username, String password) throws AuthenticationException {// 1.将SecurityManager对象设置到运行环境中SecurityUtils.setSecurityManager(securityManager);// 2.获取Subject对象Subject subject = SecurityUtils.getSubject();// 3.将前端传来的用户名密码封装为Shiro提供的身份对象UsernamePasswordToken token = new UsernamePasswordToken(username, password);// 4.Shiro认证subject.login(token);}
}

3.编写登录控制器方法

@RequestMapping("/user/login2")
public String login2(String username,String password){try {usersService.userLogin(username,password);return "main";}catch (AuthenticationException e){return "fail";}
}

多Realm认证

在实际的开发中,我们的认真逻辑可能不止一种,例如普通用户登录和管理员登录。

1.在数据库创建admin表

CREATE TABLE `admin` (`id` int(11) NOT NULL,`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;INSERT INTO `admin` VALUES (1, 'lisi', '123');
INSERT INTO `admin` VALUES (2, 'wangwu', '456');

2.创建Admin实体类和AdminMapper接口

@Data
public class Admin {private Integer id;private String name;private String password;
}public interface AdminMapper extends BaseMapper<Admin> {
}

3.MyRealm认证User用户,MyRealm2认证Admin用户

public class MyRealm2 extends AuthorizingRealm {@Autowiredprivate AdminMapper adminMapper;// 自定义认证方法@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {// 1.获取输入的管理员名UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();// 2.根据管理员名查询管理员QueryWrapper<Admin> wrapper = new QueryWrapper<Admin>().eq("name", username);Admin admin = adminMapper.selectOne(wrapper);// 3.将查询到的管理员封装为认证信息if (admin == null) {throw new UnknownAccountException("账户不存在");}/*** 参数1:管理员* 参数2:密码* 参数3:Realm名*/return new SimpleAuthenticationInfo(admin,admin.getPassword(),"myRealm2");}// 自定义授权方法@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}
}

4.在SecurityManager中配置Realm

// Realm
@Bean
public MyRealm getMyRealm() {return new MyRealm();
}
@Bean
public MyRealm2 getMyRealm2() {return new MyRealm2();
}// SecurityManager对象
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm realm, MyRealm2 realm2){DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();// Realm放入SecurityManager中List<Realm> realms = new ArrayList();realms.add(realm);realms.add(realm2);defaultSecurityManager.setRealms(realms);return defaultSecurityManager;
}

多Realm认证策略

多Realm 认证策略是一种用于身份验证和授权的安全策略,它允许在一个系统或应用程序中使用多个身份验证领域(Realm)来验证用户的身份。认证策略主要使用的是 AuthenticationStrategy接口,这个接口有三个实现类:

策略意义
AtLeastOneSuccessfulStrategy(默认)只要有一个Realm验证成功即可,返回所有成功的认证信息
FirstSuccessfulStrategy只要有一个Realm验证成功即可,只返回第一个成功的认证信息,其他的忽略
AllSuccessfulStrategy所有Realm验证成功才算成功,如果有一个失败则认证失败
// Realm管理者
@Bean
public ModularRealmAuthenticator modularRealmAuthenticator(){ModularRealmAuthenticator modularRealmAuthenticator = new ModularRealmAuthenticator();//设置认证策略modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());return modularRealmAuthenticator;
}// SecurityManager对象
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm realm, MyRealm2 realm2){DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();// 设置Realm管理者(需要在设置Realm之前)defaultSecurityManager.setAuthenticator(modularRealmAuthenticator());List<Realm> realms = new ArrayList();realms.add(realm);realms.add(realm2);defaultSecurityManager.setRealms(realms);return defaultSecurityManager;
}

异常处理

当Shiro认证失败后,会抛出AuthorizationException异常,该异常有很多子类,不同的子类意味着认证失败的原本不同,我们可以通过捕获异常确定认证失败的原因。

异常原因
DisabledAccountException账户失效
ConcurrentAccessException竞争次数过多
ExcessiveAttemptsException尝试次数过多
UnknownAccountException用户名不正确
IncorrectCredentialsException凭证(密码)不正确
ExpiredCredentialsException凭证过期
@RequestMapping("/user/login2")
public String login(String username, String password) {try {usersService.userLogin(username, password);return "main";} catch (DisabledAccountException e) {System.out.println("账户失效");return "fail";} catch (ConcurrentAccessException e) {System.out.println("竞争次数过多");return "fail";} catch (ExcessiveAttemptsException e) {System.out.println("尝试次数过多");return "fail";} catch (UnknownAccountException e) {System.out.println("用户名不正确");return "fail";} catch (IncorrectCredentialsException e) {System.out.println("密码不正确");return "fail";} catch (ExpiredCredentialsException e) {System.out.println("凭证过期");return "fail";}
}

使用散列算法加密认证

散列算法(Hash Algorithm)是一种用于数据加密、数据完整性验证和数字签名等领域的算法。它的主要目标是将任意长度的数据映射为固定长度的散列值,以便于数据的处理和比较。常见的散列算法包括 MD5、SHA-1、SHA-256 等。这些算法在计算机安全领域得到广泛应用,例如用于文件校验、密码存储、数字签名等。
例如,密码“zhangsan”会产生散列值“23ds2i1sdda97sad8u8asda0d”,但通过 md5 解密网站可以很容易地通过散列值获取到密码“zhangsan”。因此,在加密时我们可以添加一些只有系统知道的干扰数据,这些干扰数据被称为“盐”,并且可以进行多次加密,这样生成的散列值就更难被破解了。

1.修改数据库和实体类,添加盐字段,并修改数据库用户密码为加盐加密后的数据。

@Data
public class Users{private Integer uid;private String username;private String password;private String salt;
}

2.修改自定义Realm

@Component
public class MyRealm extends AuthorizingRealm {@Autowiredprivate UserInfoMapper userInfoMapper;// 自定义认证方法@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {// 1.获取用户输入的用户名UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();// 2.根据用户名查询用户QueryWrapper<Users> wrapper = new QueryWrapper<Users>().eq("username", username);Users users = usersMapper.selectOne(wrapper);// 3.将查询到的用户封装为认证信息if (users == null) {throw new UnknownAccountException("账户不存在");}/*** 参数1:用户* 参数2:密码* 参数3:盐* 参数4:Realm名*/return new SimpleAuthenticationInfo(users,users.getPassword(),ByteSource.Util.bytes(users.getSalt()),"myRealm");}// 自定义授权方法@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}
}

3.在注册自定义Realm时添加加密算法

// 配置加密算法
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();//加密算法hashedCredentialsMatcher.setHashAlgorithmName("md5");//加密的次数hashedCredentialsMatcher.setHashIterations(5);return hashedCredentialsMatcher;
}// Realm
@Bean
public MyRealm getMyRealm(HashedCredentialsMatcher hashedCredentialsMatcher) {MyRealm myRealm = new MyRealm();// 设置加密算法myRealm.setCredentialsMatcher(hashedCredentialsMatcher);return myRealm;
}

过滤器

通过配置过滤器,我们可以规定哪些资源是需要认证后才能访问的,哪些资源是不需要认证便可以访问的。Shiro内置了很多过滤器:

过滤器说明
anon配置不需要登录即可访问的资源
authc配置登录认证后才可以访问的资源
user配置登录认证或“记住我”认证后才可以访问的资源
// 配置过滤器
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){// 1.创建过滤器工厂ShiroFilterFactoryBean filterFactory=new ShiroFilterFactoryBean();// 2.过滤器工厂设置SecurityManagerfilterFactory.setSecurityManager(securityManager);// 3.设置shiro的拦截规则Map<String,String> filterMap=new HashMap<>();// 不拦截的资源filterMap.put("/login.html","anon");filterMap.put("/fail.html","anon");filterMap.put("/user/login","anon");filterMap.put("/css/**","anon");// 其余资源都需要用户认证filterMap.put("/**","authc");// 4.将拦截规则设置给过滤器工厂filterFactory.setFilterChainDefinitionMap(filterMap);// 5.登录页面filterFactory.setLoginUrl("/login.html");return filterFactory;
}

获取认证数据

通过subject.getPrincipal();我们可以获得认证后的用户数据

@RequestMapping("/user/getUsername")
@ResponseBody
public String getUsername(){Subject subject = SecurityUtils.getSubject();// 获取认证数据Users users = (Users)subject.getPrincipal();return users.getUsername();
}

Shiro会话

Shiro提供了完整的企业级会话管理功能,并且不依赖于Web容器,不管JavaSE还是JavaEE环境都可以使用。

// 使用Shiro提供的会话对象
@RequestMapping("/user/session")
@ResponseBody
public void session(){// 1.获取SubjectSubject subject = SecurityUtils.getSubject();// 2.获取会话Session session = subject.getSession();// 会话idSystem.out.println("会话id:"+session.getId());// 会话的主机地址System.out.println("会话的主机地址:"+session.getHost());// 设置会话过期时间session.setTimeout(1000*10);// 获取会话过期时间System.out.println("会话过期时间:"+session.getTimeout());// 会话开始时间System.out.println("会话开始时间:"+session.getStartTimestamp());// 会话最后访问时间System.out.println("会话最后访问时间:"+session.getLastAccessTime());// 会话设置数据session.setAttribute("name","百战");
}@RequestMapping("/user/getSession")
@ResponseBody
public void getSession(){Subject subject = SecurityUtils.getSubject();Session session = subject.getSession();System.out.println(session.getAttribute("name"));
}

会话管理器

会话管理器可以对会话对象进行配置和监听

1.创建会话监听器

@Component
public class MySessionListener implements SessionListener {//会话创建时触发@Overridepublic void onStart(Session session) {System.out.println("会话创建:" + session.getId());}//会话过期时触发@Overridepublic void onExpiration(Session session) {System.out.println("会话过期:" + session.getId());}//退出/会话过期时触发@Overridepublic void onStop(Session session) {System.out.println("会话停止:" + session.getId());}
}

2.在会话管理器中配置会话监听器

// 会话管理器
@Bean
public SessionManager sessionManager(MySessionListener sessionListener) {// 创建会话管理器DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();// 创建会话监听器集合List<SessionListener> listeners = new ArrayList();listeners.add(sessionListener);// 将监听器集合设置到会话管理器中sessionManager.setSessionListeners(listeners);// 全局会话超时时间(单位毫秒),默认30分钟,设置为5秒sessionManager.setGlobalSessionTimeout(5*1000);// 是否开启删除无效的session对象,默认为truesessionManager.setDeleteInvalidSessions(true);// 是否开启定时调度器进行检测过期session,默认为truesessionManager.setSessionValidationSchedulerEnabled(true);return sessionManager;
}

3.在SecurityManager中配置会话管理器

@Bean
public DefaultWebSecurityManager securityManager(MyRealm myRealm,MyRealm2 myRealm2,SessionManager sessionManager){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 自定义Realm放入SecurityManager中//     securityManager.setRealm(myRealm);// 设置Realm管理者(需要设置在Realm之前)securityManager.setAuthenticator(modularRealmAuthenticator());List<Realm> realms = new ArrayList();realms.add(myRealm);//     realms.add(myRealm2);securityManager.setRealms(realms);securityManager.setSessionManager(sessionManager);return securityManager;
}

退出登录

当退出登陆后,要删除会话对象和认真数据

1.编写退出登录控制器

@RequestMapping("/user/logout")
public String logout(){Subject subject = SecurityUtils.getSubject();// 退出登录subject.logout();// 退出后跳转到登录页return "redirect:/login";
}

2.在主页面添加退出登录按钮

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>主页面</title>
</head>
<body>
<h1>主页面</h1>
<h2><a href="/user/logout">退出登录</a></h2>
</body>
</html>

记住我

Shiro的记住我功能可以在用户登录成功后,关闭浏览器,下次再访问系统资源时,无需再执行登录操作。
实现Shiro的记住我功能,一般是通过将用户的一些基本信息(密码)存入浏览器的Cookie,下次登录时优先验证Cookie,后端做处理,以此来实现记住密码的功能。
需注意,若网站对安全性要求较高,一般不建议开启记住密码功能,因为Cookie是保存在本机电脑浏览器里,其他用户可能会使用此电脑拷走Cookie,导入其他电脑继续使用账号登录。

1.序列化所有实体类

@Data
public class Users implements Serializable {private Integer uid;private String username;private String password;private String salt;
}

2.配置Cookie生成器和记住我管理器

// Cookie生成器
@Bean
public SimpleCookie simpleCookie() {SimpleCookie simpleCookie = new SimpleCookie("rememberMe");// Cookie有效时间,单位:秒simpleCookie.setMaxAge(20);return simpleCookie;
}// 记住我管理器
@Bean
public CookieRememberMeManager cookieRememberMeManager(SimpleCookie simpleCookie) {CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();// Cookie生成器cookieRememberMeManager.setCookie(simpleCookie);// Cookie加密的密钥cookieRememberMeManager.setCipherKey(Base64.decode("6ZmI6I2j3Y+R1aSn5BOlAA=="));return cookieRememberMeManager;
}@Bean
public DefaultWebSecurityManager securityManager(MyRealm myRealm,MyRealm2 myRealm2,SessionManager sessionManager,CookieRememberMeManager rememberMeManager){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 自定义Realm放入SecurityManager中//     securityManager.setRealm(myRealm);// 设置Realm管理者(需要设置在Realm之前)securityManager.setAuthenticator(modularRealmAuthenticator());List<Realm> realms = new ArrayList();realms.add(myRealm);//     realms.add(myRealm2);securityManager.setRealms(realms);securityManager.setSessionManager(sessionManager);securityManager.setRememberMeManager(rememberMeManager);return securityManager;
}

3.修改登录表单

<form class="form" action="/user/login" method="post"><input type="text" placeholder="用户名" name="username"><input type="password" placeholder="密码" name="password"><input type="checkbox" name="rememberMe" value="on">记住我<br><button type="submit" id="login-button">登录</button>
</form>

4.修改登录控制器

@RequestMapping("/user/login")
public String login(String username, String password,String rememberMe) {try {usersService.userLogin(username, password,rememberMe);return "main";} catch (DisabledAccountException e) {System.out.println("账户失效");return "fail";} catch (ConcurrentAccessException e) {System.out.println("竞争次数过多");return "fail";} catch (ExcessiveAttemptsException e) {System.out.println("尝试次数过多");return "fail";} catch (UnknownAccountException e) {System.out.println("用户名不正确");return "fail";} catch (IncorrectCredentialsException e) {System.out.println("密码不正确");return "fail";} catch (ExpiredCredentialsException e) {System.out.println("凭证过期");return "fail";}
}

5.修改登录Service

@Service
public class UsersService {@Autowiredprivate DefaultWebSecurityManager securityManager;public void userLogin(String username,String password,String rememberMe) throws AuthenticationException {SecurityUtils.setSecurityManager(securityManager);Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token=new UsernamePasswordToken(username,password);if (rememberMe != null){// 如果用户选择记住我,则生成记住我Cookietoken.setRememberMe(true);}subject.login(token);}
}

6.配置过滤器,配置可以通过“记住我”访问的资源。

@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){// 1.创建过滤器工厂ShiroFilterFactoryBean filterFactory=new ShiroFilterFactoryBean();// 2.过滤器工厂设置SecurityManagerfilterFactory.setSecurityManager(securityManager);// 3.设置shiro的拦截规则Map<String,String> filterMap=new HashMap<>();// 不拦截的资源filterMap.put("/login.html","anon");filterMap.put("/fail.html","anon");filterMap.put("/user/login","anon");filterMap.put("/static/**","anon");// 其余资源都需要认证,authc过滤器表示需要认证才能进行访问; user过滤器表示配置记住我或认证都可以访问//     filterMap.put("/**","authc");filterMap.put("/user/pay","authc");filterMap.put("/**", "user");// 4.将拦截规则设置给过滤器工厂filterFactory.setFilterChainDefinitionMap(filterMap);// 5.登录页面filterFactory.setLoginUrl("/login.html");return filterFactory;
}

7.编写支付控制器

// 支付
@RequestMapping("/user/pay")
@ResponseBody
public String pay(){return "支付功能";
}

总结

提示:这里对文章进行总结:

Shiro是一个简单且强大的框架,提供了全面的安全管理服务,如认证、授权、加密和会话管理等。总的来说,Shiro的认证功能可以通过realm来实现,并且可以通过自定义realm来满足不同的认证需求。在使用Shiro时,建议遵循最佳实践,并根据你的项目需求进行适当的配置和定制,以确保安全性和灵活性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/604922.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【VTKExamples::Visualization】第一期 Arbitrary3DCursor

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ&#xff1a;870202403 前言 本文分享Example中Visualization模块中的Arbitrary3DCursor样例&#xff0c;主要解析vtkProbefileter&#xff0c;希望对各位小伙伴有所帮助&#xff01; 感谢各位小伙伴的点赞关注&#xff0c;小易会…

【Docker基础二】Docker安装Mysql8

下载镜像 安装mysql&#xff08;版本&#xff1a;8.0.35&#xff09; # 拉取镜像 docker pull mysql:8.0.35 # 查看镜像是否已经下载 docker images 创建挂载目录 # 宿主机上创建挂载目录 (可以不创建&#xff0c;docker run -v配置了挂载目录&#xff0c;docker会自动…

Priors in Deep Image Restoration and Enhancement: A Survey

深度图像恢复和增强中的先验&#xff1a;综述 论文链接&#xff1a;https://arxiv.org/abs/2206.02070 项目链接&#xff1a;https://github.com/VLIS2022/Awesome-Image-Prior (Preprint. Under review) Abstract 图像恢复和增强是通过消除诸如噪声、模糊和分辨率退化等退化…

【python入门】day21:向文件输出“奋斗成就更好的你”、输出北京的天气预报

向文件输出“奋斗成就更好的你” #向文件输出‘奋斗成就更好的你’ 第一种方式&#xff1a;使用print方式进行输出&#xff08;输出目的地是文件&#xff09; fpopen(e:/text.txt,w)#w只写模式&#xff0c;也可以用a读写模式 print(奋斗成就更好的你,filefp) fp.close() 第二种…

python统计分析——箱线图(plt.boxplot)

参考资料&#xff1a;用python动手学统计学 使用matplotlib.pyplot.boxplot()函数绘制箱线图 import numpy as np import pandas as pd from matplotlib import pyplot as pltdata_set1np.array([2,3,3,4,4,4,4,5,5,6]) data_set2np.array([[2,3,3,4,4,4,4,5,5,6],[5,6,6,7,7…

Linux程序、进程和计划任务

目录 一.程序和进程 1.程序的概念 2.进程的概念 3.线程的概念 4.单线程与多线程 5.进程的状态 二.查看进程信息相关命令&#xff1a; 1.ps&#xff1a;查看静态进程信息状态 2.top&#xff1a;查看动态进程排名信息 3.pgrep&#xff1a;查看指定进程 4.pstree&#…

使用 pdfh5 实现 pdf 预览功能

1. 安装 npm install pdfh5 2. 使用 html部分&#xff1a; <div id"showPdf" style"width: 100%;"></div> js部分&#xff1a; <script> //合同展示组件 import Pdfh5 from pdfh5 //合同组件样式 import pdfh5/css/pdfh5.css expo…

Educational Codeforces Round 160 (Rated for Div. 2) D. Array Collapse(单调栈+dp)

题目 给定一个长为n(n<2e5)的排列&#xff0c;你可以执行以下操作若干次&#xff0c; 每次你可以选择一个区间[l,r]&#xff0c;只保留这个区间内的最小值&#xff0c;将其他值都删除 删完之后前后位置会自动接上&#xff0c;形成一个新的数组 求这样操作若干次后&#…

docker 安装elasticsearch、kibana、cerebro、logstash

安装步骤 第一步安装 docker 第二步 拉取elasticsearch、kibana、cerebro、logstash 镜像 docker pull docker.elastic.co/elasticsearch/elasticsearch:7.10.2 docker pull docker.elastic.co/kibana/kibana:7.10.2 docker pull lmenezes/cerebro:latest docker pull l…

Ps:混合颜色带 - 应用篇

混合颜色带 Blend If是基于亮度&#xff08;灰色&#xff09;或颜色通道的特定范围来显示或隐藏图层的特定区域。 当前图层 Current Layer&#xff0c;可根据当前图层的亮度值来隐藏该图层中的像素。 下一图层 Underlying Layer&#xff0c;可根据下方所有图层的复合图像的亮度…

地理空间分析5——空间关联分析与Python

目录 写在开头1.空间自相关2.空间回归分析2.1 构建地理权重矩阵2.2 执行空间回归分析2.3 解释结果3 地理加权回归3.1 构建地理权重矩阵3.2 执行地理加权回归分析3.3 解释地理加权回归结果写在最后写在开头 空间关联分析是数据科学领域中一个重要的技术,尤其在地理信息系统(G…

数据密集型应用系统设计--3.1 数据库核心:数据结构

3.1 数据库核心&#xff1a;数据结构 数据库只需做两件事情&#xff1a;向它插入数据肘&#xff0c;它就保存数据&#xff1a;之后查询时&#xff0c;它应该返回那些数据。 本章我们主要从数据库的角度再来探讨同样的问题&#xff0c;即如何存储输入的数据&#xff0c;井在收到…

单向可控硅充电电路图

单向可控硅工作原理 单向可控硅有阳极A、阴极K、控制极G三个电极&#xff0c;由四层半导体PNPN构成。单向可控硅有三个PN结&#xff0c;其内部结构与等效电路符号如图4-10所示。单相可控硅可等效看成一个PNP型三极管Vl和一个NPN型三极管V2组合而成&#xff0c;Vl基极和V2集电极…

电子学会C/C++编程等级考试2023年12月(三级)真题解析

C/C++编程(1~8级)全部真题・点这里 第1题:因子问题 任给两个正整数N、M,求一个最小的正整数a,使得a和(M-a)都是N的因子。 时间限制:10000 内存限制:65536 输入 包括两个整数N、M。N不超过1,000,000。 输出 输出一个整数a,表示结果。如果某个案例中满足条件的正整数不存…

MSCKF+OpenVins梳理

reference&#xff1a; openvins学习中的问题https://zhuanlan.zhihu.com/p/355319559 OpenVins代码梳理https://www.zhihu.com/people/anson2004110/posts OpenVINS能观一致性分析和FEJhttps://zhuanlan.zhihu.com/p/101478814 MSCKF那些事https://zhuanlan.zhihu.com/p/76894…

React16源码: React中FiberRoot的源码实现

关于 FiberRoot 1 &#xff09;概述 在 ReactDOM.render 过程当中&#xff0c;创建了一个 ReactRoot的对象这个 ReactRoot 对象最主要承担了创建 FiberRoot 对象这个对象它非常重要&#xff0c;在后期整个应用调度过程当中都会跟它有关关于 FiberRoot 对象 A. 它是整个应用的起…

es6中箭头函数 原型

箭头函数 特性 箭头函数的特性 不绑定arguments&#xff0c;用rest参数…解决本身没有this的概念&#xff0c;捕获其所在上下文的 this 值作为自己的 this 值&#xff0c;this指向全局箭头函数不能使用new&#xff08;会报错&#xff09;箭头函数没有原型属性(prototype)箭头函…

力扣labuladong一刷day54天前缀树

力扣labuladong一刷day54天前缀树 文章目录 力扣labuladong一刷day54天前缀树一、208. 实现 Trie (前缀树)二、648. 单词替换三、211. 添加与搜索单词 - 数据结构设计四、1804. 实现 Trie &#xff08;前缀树&#xff09; II五、677. 键值映射 一、208. 实现 Trie (前缀树) 题…

linux源码解读系列

Linux 源码解读是一个深入且复杂的领域&#xff0c;涵盖了操作系统的核心部分&#xff0c;包括内核架构、进程管理、内存管理、文件系统、网络通信等。由于 Linux 内核源码庞大且复杂&#xff0c;通常需要具备扎实的操作系统理论知识和C语言编程能力。下面是一些推荐的资源和方…

一键了解获取网页requests方式

目录 一、爬虫原理&#xff1a; 二、安装&#xff1a; 测试&#xff1a; 三、文件的操作 方式一 方式二: 方式三 四、认识User-Agent 4.1、为什么用User-Agent&#xff1a; 步骤&#xff1a; 五、请求方式 5.1、get 5.2、post 六、爬出有中国关键字页面案例 一、爬…