系列文章目录
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
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时,建议遵循最佳实践,并根据你的项目需求进行适当的配置和定制,以确保安全性和灵活性。