Shiro介绍
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
主要功能
三个核心组件:Subject, SecurityManager 和 Realms.
- Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。 - SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
- Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
使用shiro整合SpringBoot
- 1.1 创建Shiro环境
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
- 1.2 配置Shiro
– 测试环境配置yml
# 开发阶段,建议关闭thymeleaf的缓存,不然没有办法看到实时画面
spring.thymeleaf.cache=false
# 去掉H5的语法验证
spring.thymeleaf.mode=LEGACYHTML5
– 定义类 config.ShiroConfig
package com.netft.config;
import com.netft.realm.MyRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author: fengT
* @date: 2020/7/23 0023 下午 15:42
* @modified by
*/
// 标记当前类是一个Spring的配置类用于模拟Spring的配置文件
@Configuration
public class ShiroConfig {
/**
* 配置一个SecurityManager 安全管理器
* @return
*/
@Bean
public SecurityManager securityManager(Realm realm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
// 配置一个自定义的Realm的bean,最终将使用这个bean返回的对象来完成我们的认证和授权
@Bean
public MyRealm myRealm(){
MyRealm myRealm = new MyRealm();
return myRealm;
}
//配置一个Shiro的过滤器bean,这个bean将配置Shiro相关的一个规则的拦截
//例如什么样的请求可以访问什么样的请求不可以访问等等
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
// 设置安全管理器
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/"); // 配置用户登录请求,如果需要进行登录时,shiro就会转到这个请求上,进入登录页面
shiroFilterFactoryBean.setSuccessUrl("/success");// 配置成功之后转向的地址
shiroFilterFactoryBean.setUnauthorizedUrl("/noPermission"); // 配置没有权限时转向的页面
/**
* 配置权限拦截规则
* anon:无需认证就可以访问
* logout:登出,清空内存
* authc:必须认证才可以访问
* user:必须拥有记住我功能才能用
* perms:拥有对某个资源的权限才能访问
* role:拥有某个角色权限才能访问
*/
LinkedHashMap<String, String> filterChainMap = new LinkedHashMap<>();
filterChainMap.put("/login","anon"); // 配置登录不需要认证
filterChainMap.put("/logout","logout"); // 登出,清空当前用户内存,释放session对话
filterChainMap.put("/admin/**","authc"); // admin开头的所有请求需要登录认证
// 配置剩余的所有请求都需要进行登录认证,必须写在最后
filterChainMap.put("/**","authc");
// 设置权限拦截规则
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
return shiroFilterFactoryBean;
}
}
– 定义MyRealm类
/**
* 自定义的realm,用来实现用户的认证和授权
* 父类 AuthorizingRealm 实现用户的认证(登录)和授权(权限)
*
*/
public class MyRealm extends AuthorizingRealm {
/**
* 认证 这个方法不能手动调用shiro会自动调用
* @param authenticationToken 用户身份 这里存放着用户的账号和密码
* @return 用户登录成功后的身份证明
* @throws AuthenticationException 如果认证失败shiro会抛出各种异常
* 常用异常
* UnknownAccountException 账号不存在
* AccountException 账号异常
* LockedAccountException 账户锁定异常(冻结异常)
* IncorrectCredentialsException 密码认证失败以后Shiro自动抛出表示密码错误
* 注意:
* 如果这些异常不够用可以自定义异常类并继承Shiro认证异常父类AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername(); // 获取页面中传递的用户账号
String password = new String(token.getPassword()); // 获取页面中的用户密码实际工作中基本不需要获取
System.out.println(username+"----"+password);
/**
* 认证账号,这里应该从数据库中获取数据
* 如果进入if表示账号不存在要抛出异常
*/
if (!"admin".equals(username)){
throw new UnknownAccountException(); // 抛出账号错误的异常
}
/**
* 认证账号,这里应该从数据库中获取数据进行逻辑判断,判断账号是否可用
* IP是否允许等等,根据不同的逻辑可以抛出不同的异常
*/
if ("zhangsan".equals(username)){
throw new LockedAccountException(); // 抛出账号锁定的异常
}
// 创建密码认证对象,有shiro自动认证密码
// 参数一:数据库中的账号(或页面账号)
// 参数二:当前帐号的密码
// 参数三:当前realm的名字
// 若果密码认证成功则返回一个用户身份对象,如果密码认证失败shiro会抛出异常 IncorrectCredentialsException
return new SimpleAuthenticationInfo(username,"123456",getName());
}
– 定义Controller
package com.netft.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author: fengT
* @date: 2020/7/27 0027 下午 21:01
* @modified by
*/
@Controller
public class TestController {
@RequestMapping("/")
public String index(){
return "login";
}
@RequestMapping("/login")
public String login(String username, String password, Model model){
// 获取权限操作对象,利用这个对象来完成登录操作
Subject subject = SecurityUtils.getSubject();
// 用户是否登录过,进入if代表用户没有认证过需要进行认证
if (!subject.isAuthenticated()){
// 创建用户认证时的身份令牌,并设置我们从页面传递的账号和密码
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password);
try {
/**
* 指定登录,会自动调用我们Realm对象中的认证方法
* 如果登录失败就会抛出各种异常
*/
subject.login(usernamePasswordToken);
}catch (UnknownAccountException e) {
// e.printStackTrace();
model.addAttribute("errorMessage","账号错误!");
return "login";
}catch (LockedAccountException e) {
// e.printStackTrace();
model.addAttribute("errorMessage","账号被锁定!");
return "login";
}catch (IncorrectCredentialsException e) {
// e.printStackTrace();
model.addAttribute("errorMessage","密码错误");
return "login";
}catch (AuthenticationException e) {
// e.printStackTrace();
model.addAttribute("errorMessage","认证失败!");
return "login";
}
}
return "redirect:/success";
}
@RequestMapping("/logout")
@ResponseBody
public String logout(){
return "redirect:/";
}
@RequestMapping("/success")
public String loginSuccess(){
return "success";
}
@RequestMapping("/noPermission")
public String noPermission(){
return "noPermission";
}
@RequestMapping("/admin/test")
@ResponseBody
public String adminTest(){
return "/admin/test";
}
@RequestMapping("/user/test")
@ResponseBody
public String userTest(){
return "/user/test";
}
}
–定义login.html,nopermission.html、success.html
// login.html
<form action="login" method="post">
账号<input type="text" name="username"><br>
密码<input type="text" name="password" id="password"><br>
<input type="submit" value="登录" id="loginBut">
</form>
<span style="color: red" th:text="${errorMessage}"></span>
// nopermission.html
<h1>没有权限请联系管理员</h1>
// success.html
<h1>登录成功</h1>
到此测试。简单的shiro认证就做完了