Shiro简介
Apache Shiro是一个强大且灵活的开源安全框架,它提供了身份验证、授权、密码学和会话管理等一系列安全相关的功能。Shiro的设计目标是易于使用,同时保持强大的安全性。它可以在任何Java应用程序中使用,无论是简单的Java SE应用程序还是复杂的Java EE企业级应用。
Shiro的四大基石
-
Authentication(认证):
- 认证,也称为登录,是验证用户身份的过程。
- 用户需要提供身份(principals)和凭证(credentials)给Shiro,以便Shiro能够验证用户的身份。
- 身份通常是用户名或电子邮件地址,而凭证通常是密码。
-
Authorization(授权):
- 授权是验证用户是否拥有访问某个资源或执行某个操作的权限的过程。
- Shiro通过Realm获取用户的角色和权限信息,并根据这些信息来判断用户是否有权访问某个资源或执行某个操作。
- 授权机制可以基于角色(RBAC)或基于权限(PBAC)来实现。
-
Session Management(会话管理):
- 会话管理是指管理用户登录后的会话信息。
- Shiro提供了会话的创建、获取、销毁和超时等管理操作。
- 通过会话管理,Shiro可以跟踪用户的登录状态和会话信息,并在用户注销或会话超时后销毁会话。
-
Cryptography(加密):
- 加密是保护数据安全的重要手段。
- Shiro提供了密码加密、散列和盐值等密码学功能,以确保用户密码和其他敏感数据的安全存储和传输。
- 加密机制可以防止数据在传输过程中被窃取或篡改,从而确保数据的完整性和保密性。
这四大基石共同构成了Shiro安全框架的基础,使得Shiro能够为用户提供全面、可靠的安全保障。通过合理的配置和使用,Shiro可以有效地提高应用程序的安全性,并保护用户的隐私和数据安全。
Shiro的核心组件
-
Subject:代表当前操作的用户,可以是任何与软件交互的实体。Subject提供了与当前用户交互的API,如登录、注销、获取会话等。
-
SecurityManager:Shiro的核心,它管理着所有的Subject,并与Shiro的其他组件进行交互。SecurityManager负责协调Shiro的安全操作,如身份验证、授权等。
-
Realm:Shiro与数据源(如数据库、LDAP等)之间的桥梁。Realm负责获取安全数据(如用户、角色、权限等),并将其提供给Shiro的其他组件使用。
SpringBoot整合Shiro架构实现认证
自定义Realm
自定义Realm需要继承AuthorizingRealm类,该类封装了很多方法,且继承自Realm类。
继承AuthorizingRealm类后,我们需要重写以下两个方法:
doGetAuthorizationInfo()方法:获取权限信息
doGetAuthenticationInfo()方法:获取身份信息
package com.ktjiaoyu.thymeleaf.config.shiro;
import com.ktjiaoyu.thymeleaf.entity.Role;
import com.ktjiaoyu.thymeleaf.entity.RoleRight;
import com.ktjiaoyu.thymeleaf.entity.User;
import com.ktjiaoyu.thymeleaf.service.UserService;
import jakarta.annotation.Resource;
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 java.util.Set;
/**
* @author cuishujian
* @date 2024/9/29
*/
public class MyShirRealm extends AuthorizingRealm {
@Resource
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("调用MyShiroRealm.doGetAuthorizationInfo获取权限信息");
// 获取权限信息
User user = (User) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 这里我们暂时先不授予权限信息,在下一个Shiro(授权Authorization)再实现
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("调用MyShiroRealm.doGetAuthenticationInfo获取身份信息!");
// 获得身份信息
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String usrName = token.getUsername();
User user = userService.getUserByUsrName(usrName);
System.out.println(user);
if (user == null){
throw new UnknownAccountException(); // 账号错误
}
if (user.getUsrFlag() == null || user.getUsrFlag().intValue() == 0){
throw new LockedAccountException(); // 账号锁定
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getUsrPassword(),getName());
return info;
}
}
配置Shiro相关对象
package com.ktjiaoyu.thymeleaf.config.shiro;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.alibaba.druid.sql.visitor.functions.Right;
import com.ktjiaoyu.thymeleaf.entity.RoleRight;
import com.ktjiaoyu.thymeleaf.service.RoleService;
import com.ktjiaoyu.thymeleaf.service.impl.RoleServiceImpl;
import jakarta.annotation.Resource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* @author cuishujian
* @date 2024/9/29
*/
@Configuration
public class ShiroConfig {
@Resource
RoleService roleService;
@Bean
public MyShirRealm myShirRealm(){ // 自定义Realm
System.out.println("【myShirRealm】");
MyShirRealm shirRealm = new MyShirRealm();
return shirRealm;
}
@Bean
public SecurityManager securityManager(){ // 安全管理器 SecurityManager
System.out.println("【securityManager】");
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 注入Realm
securityManager.setRealm(myShirRealm());
SecurityUtils.setSecurityManager(securityManager);
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager){ // Shiro过滤器:权限验证
System.out.println("【shiroFilterFactory】");
ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();
// 注入SecurityManager
shiroFilterFactory.setSecurityManager(securityManager);
// 这里我们暂时先不验证权限信息,在下一个Shiro(授权Authorization)再实现
return shiroFilterFactory;
}
@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
/**
* 开启 Shiro注解 (如 @RequiresRoles,@RequiresPermissions)
* 需借助 SpringAOP 扫描使用 Shiro 注解的类,并在必要时进行安排逻辑验证
* 配置以下两个bean(DefaultAdvisorAutoProxyCreator和 AuthorizationAttributeSourceAdvisor)
*/
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 开启 aop 注解支持
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
编写控制器Controller
package com.ktjiaoyu.thymeleaf.controller;
import com.ktjiaoyu.thymeleaf.service.RightService;
import com.ktjiaoyu.thymeleaf.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author cuishujian
* @date 2024/9/13
*/
@Controller
public class IndexController {
@RequestMapping("/login")
public String login(){
return "login";
}
@RequestMapping("/main")
public String toMain(Model model){
return "main";
}
@RequestMapping("/doLogin")
public String doLogin(String usrName, String usrPassword, HttpServletRequest request, Model model){
try {
UsernamePasswordToken token = new UsernamePasswordToken(usrName,usrPassword);
Subject subject = SecurityUtils.getSubject();
subject.login(token);// 认证、登录
// 认证(登录)成功
User user = (User) subject.getPrincipal();
request.getSession().setAttribute("loginUser",user);
return "redirect:/main";
}catch (UnknownAccountException | IncorrectCredentialsException e){
model.addAttribute("msg","用户名或密码错误,登录失败!");
return "login";
}catch (LockedAccountException e){
model.addAttribute("msg","用户被禁用,登录失败!");
return "login";
}catch (AuthenticationException e){
model.addAttribute("msg","认证异常,登录失败!");
return "login";
}
}
}