Shiro 应用开发
Shiro 的登录身份验证流程:
- 用户提供登录凭证(通常是用户名和密码)。
- 应用程序调用 Shiro 的身份验证 API,并将用户提供的凭证传递给 Shiro。
- Shiro 使用配置好的身份验证器,如用户名密码验证器,对用户凭证进行验证。
- 如果凭证有效,Shiro 将返回一个身份验证成功的结果,并创建一个主体 Subject 来代表已验证的用户。
- 应用程序可以根据需要访问主体对象,获取用户的身份信息。
- 如果凭证无效,Shiro 将返回一个身份验证失败的结果,应用程序可以根据需要处理失败情况。
总结来说,Shiro 的登录身份验证流程包括用户提供凭证、Shiro 进行验证、返回验证结果和创建主体对象。这
样,应用程序可以根据验证结果来控制用户的访问权限和行为。
Shiro 授权验证流程: - 用户尝试执行某个操作,需要进行授权验证。
- 应用程序调用 Shiro 的授权 API,并传递用户信息和操作的权限要求。
- Shiro 根据用户信息和权限要求,判断用户是否具有执行该操作的权限。
- 如果用户具有足够的权限,Shiro 将返回授权成功的结果,应用程序可以继续执行操作。
- 如果用户权限不足,Shiro 将返回授权失败的结果,应用程序可以根据需要处理失败情况。
在授权验证过程中,Shiro 可以根据配置的角色和权限信息来判断用户是否具有相应的权限。它还支持细粒度
的权限控制,可以对具体的操作进行授权,以确保系统的安全性和可靠性。
总结来说,Shiro 的授权验证流程包括应用程序调用授权 API、Shiro 判断用户权限、返回授权结果。这样应用程序可以根据授权结果来决定用户是否可以执行某个操作。
1、添加依赖
2、定义数据源,就是用于获取用户的相关信息
通过 ShiroFilter 拦截所以后请求,再到安全管理器处理请求 SecurityManager,然后通过 Realm 获取数据库数
据进行认证授权校验。
Realm 是 Shiro 和应用程序之间的一个桥梁,用于在认证和授权时对安全数据的对比和校验。如在进行登录验证时数据对比是在 Realm,一般默认 Realm 继承了 AuthorizingRealm 的 doGetAuthenticationInfo 中实现,在授权时对用户角色的权限的判定也将在 doGetAuthenticationInfo 中实现
@Component
public class CustomRealm extends AuthorizingRealm {
@Autowired
private IUserServ userService;
@Autowired
private IRoleServ roleService;
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { 鉴权。主要是获
取用户角色和权限,并交给 Shiro 去判断是否具有访问资源的权限
System.out.println("需要权限验证时,才会执行");
SimpleAuthorizationInfo info = null;
Object obj = principalCollection.getPrimaryPrincipal();
System.out.println("author:"+obj);
if (obj != null) {
info = new SimpleAuthorizationInfo();
String username = obj.toString();
Set<Role> roleSet = userService.loadRoleByName(username);
if (roleSet != null && roleSet.size() > 0)
for (Role tmp : roleSet) {
info.addRole(tmp.getTitle());
Set<Permission> permissionSet = roleService.loadPermissionByRoleId(tmp.getId());
for (Permission tmp1 : permissionSet)
info.addStringPermission(tmp1.getPermissionString());
}
}
return info;
}
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws
AuthenticationException { //认证,主要作用就是通过用户名称获取对应的口令信息交给 shiro 框架。定义如何获取用户信息的业务逻辑,由 shiro 的密码匹配器进行匹配。
System.out.println("登录系统");
SimpleAuthenticationInfo info = null;
若用户不存在,可抛出 UnknownAccountException 异常 if(u==null) throw new UnknownAccountException(" 用户不存在"); 用户未激活则 DisabledAccountException 异常;若用户被锁定,可抛出 LockedAccountException
异常 if(u.isLocked()) throw new LockedAccountException("用户被锁定");
if (authenticationToken != null && authenticationToken instanceof UsernamePasswordToken) {
UsernamePasswordToken token=(UsernamePasswordToken)authenticationToken;
String username = token.getUsername();
User user = userService.selectByName(username); 实际项目中这里可以根据实际情况做缓
存,如果不做,Shiro 自己也是有时间间隔机制,2 分钟内不会重复执行该方法
if (user != null)
info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
可以添加第三个参数为盐值 ByteSource.Util.bytes(salt)
}
return info;
}
}
自定义配置
@Configuration
public class ShiroConfig {
@Bean
public CustomRealm myShiroRealm() { 将自己的验证方式加入容器
CustomRealm customRealm = new CustomRealm();
return customRealm;
}
//默认的 web 安全管理器 DefaultWebSecurityManager
@Bean
public DefaultWebSecurityManager getdefaultWebSecurityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm()); 关联 userRealm
return securityManager;
}
//Filter 工厂设置对应的过滤条件和跳转条件,
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(org.apache.shiro.mgt.SecurityManager
securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
Map<String, String> map = new LinkedHashMap<>();
map.put("/logout", "logout");
map.put("/login","anon");
map.put("/swagger-ui.html", "anon"); swagger 接口权限开放
map.put("/**","authc");
factoryBean.setFilterChainDefinitionMap(map);
return factoryBean;
}
}
核心配置
1、loginUrl 没有登录的用户,请求某个资源页面时自动跳转到制定的页面。
2、unauthorizedUrl 登录成功的用户访问了没有被授权的资源,自动跳转到制定的页面。
3、map.put(“/**”, “authc”)使得所有请求都需要认证才能请求成功,否则就跳转至 setLoginUrl 中指定的页面,同时为了使登录页面可以在未登陆时请求成功,需要用代码 map.put(“/login.html”,“anon”)进行设置。
控制器用户认证信息
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("username", "password");
try { //进行验证,这里可以捕获异常,然后返回对应信息
subject.login(token);
// subject.checkRole("admin");
// subject.checkPermissions("query", "add");
} catch (UnknownAccountException e) {
return "用户名不存在!";
} catch (AuthenticationException e) {
return "账号或密码错误!";
} catch (AuthorizationException e) {
return "没有权限";
}
return "login success";
调用 Subject 的 login 方法时,shiro 会调用 SecurityManage 安全管理器,尝试对比密码并登录,而编程要做的就是自定义 Realm,应用中的角色、用户、权限都是从 Realm 得到的数据,也就是说 SecurityManage 认证一定会从 Realm 取用户的一些信息,可以把它理解为数据源。
方法上添加注解权限检查配置可在控制器方法上添加的注解
@RequiresAuthentication 表示 subject 已经通过登录验证,才可使用
@RequiresUser 表示 subject 已经身份验证或者通过记住我登录,才可使用
@RequiresGuest 表示 subject 没有身份验证或通过记住我登录过,即是游客身份,才可使用
@RequiresRoles(value={“admin”, “user”}, logical=Logical.AND) 表示 subject 需要 xx(value)角色,才可使用
@RequiresPermissions (value={“user:a”, “user:b”},logical= Logical.OR)表示 subject 需要 xxx(value)权限,才可使用
@RequiresRoles({“admin”,“VIP”}) 定义调用该方法的用户必须具有 admin 和 VIP 两个角色
@RequiresPermissions(value = {“manager:users:list”, “manager:users:add”}, logical = Logical.AND) 声明调
用该方法的用户必须具有 manager:users:list 和 manager:users:add 权限才可以防范
常见的拦截过滤器可以通过用这些过滤器来配置控制指定 url 的权限
1、配置缩写 anon 对应过滤器为 AnonymousFilter,用于指定 url 可以匿名访问,访问时不需要认证授权
2、缩写 authc 对应过滤器 FormAuthenticationFilter,用于指定 url 需要 form 表单登录,默认会从请求中获取
username、password、rememberMe 等参数并尝试登录,如果登录不了就会跳转到 loginUrl 配置的路径。也
可以用这个过滤器做默认的登录逻辑,但是一般都是自己在控制器写登录逻辑的,自定义的出错返回的信息都
可以定制
3、perms 对应 PermissionsAuthorizationFilter,需要指定权限才能访问
4、roles 对应 RolesAuthorizationFilter,需要指定角色才能访问
额外开启对 shiro 注解的支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor
(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(defaultWebSecurityManager);
return advisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
autoProxyCreator.setProxyTargetClass(true);
return autoProxyCreator;
}
登录失败处理
@ExceptionHandler({AuthorizationException.class, UnauthorizedException.class})
public String authorizationException(Exception e,Model model) {
model.addAttribute("msg",e.getMessage());
return "users/login";
}