Shiro安全框架简介

这篇具有很好参考价值的文章主要介绍了Shiro安全框架简介。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

 一、权限管理

1.1 什么是权限管理

  • 基本上只要涉及到用户参数的系统都要进行权限管理,使用权限管理实现了对用户访问系统的控制,不同的用户访问不同的资源。按照安全规则或者安全策略控制用户访问资源,而且只能访问被授权的资源
  • 权限管理包括认证和授权两部分,当用户访问资源时先对其进行身份的认证,认证通过后即可访问已经授权的资源。

1.2 身份认证

  • 用来判断一个用户是否合法的处理过程。用过用户输入的用户名或者口令来和系统中存储进行比较,从而认证用户的身份是否正确

对于身份认证,也就是之前做的的登录

1.3 授权

  • 用来控制认证过后的用户可以访问哪些资源。用户身份认证后需要给该用户分配可访问的资源,如果没有某个资源的权限,那么将无法访问

二、Shiro架构

2.1 Shiro的理解

是一个功能强大且易实现的Java安全框架,使用Shiro可以执行认证、授权、加密和会话管理。使用Shiro中提供的API可以快速轻松的保护任何程序

Shiro不依赖于WEB,即使是一个测试程序也能够使用Shiro中的功能

2.2 Shiro的体系

官方图示:Shiro安全框架简介

原理:

Shiro安全框架简介

Subject

  • 表示主体,外部应用和Subject进行交互。Subject中记录了当前操作用户,这个用户可以是一个发送请求的用户,也可以是一个运行的程序
  • Subject在Shiro是一个接口,定义了很多认证授权的相关方法,外部程序通过Subject进行认证授权,Subject又是通过SecurityManager安全管理器就行认证管理

SecurityManager

  • 表示安全管理器,是Shiro中的核心,用来协调其托管的组件,以保证它们能够顺利协同工作。
  • 可以进行会话管理等

Authenticator

  • 表示身份认证器,负责执行和响应用户的身份认证尝试的组件。当用户进行登录操作时,此组件进行处理

Authorizer

  • 表示授权器,负责控制用户在系统中可以访问哪些资源。在访问资源时都需要该组件进行判断当前用户是否拥有这个资源的权限

Realm

  • 表示领域,充当Shiro与应用程序的安全数据之间的桥梁或者连接器,和DataSource数据源差不多,当需要和安全相关的数据(如用户账号)进行实际交互从而执行认证和授权时,Shiro会从应用配置的一个或者多个Realm中查询其中的内容
  • SecurityManager进行安全认证时需要通过Realm获取到用户权限数据
  • Realm不只是从数据库取数据,还有认证和授权的相关逻辑代码

SessionManager

  • 表示会话管理,知道如果创建和管理用户生命周期,以便为所欲环境中的用户提供强大的会话体验
  • 不依赖WEB容器,所以Shiro可以使用在非WEB应用中也可以将分布式应用的会话集中在一点管理,次特征可以使它实现单点登录

SessionDao

  • 表示会话Dao,是对session会话操作的一套接口

CacheManager

  • 表示缓存管理器,将用户权限存储到缓存中,从而提高性能

Cryptography

  • 表示密码管理,Shiro中提供了一套加密/解密的组件,方便开发

三、Shiro中的认证

3.1 认证中的关键对象

  • Subject:主体

访问系统的每一个用户或者应用程序,经过认证的都成为主体

  • Principal:身份信息

是主体(Subject)进行身份证认证的表示,表示必须具有唯一性。比如用户名/手机号/邮箱,一个主体(Subject)中可以有多个身份信息,但必须有一个主身份

  • Credential:凭证信息

3.2 认证流程

图示:

Shiro安全框架简介

文字:

  1. 收集使用者的Principal和Credential
  1. 提交进行身份验证
  1. 如果验证成功就允许访问,否则重试身份证验证或者阻止访问

3.3 环境搭建

  1. 引入Shiro依赖
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.9.1</version>
</dependency>
  1. 在resources下创建.ini的配置文件,来临时模拟数据库存储用户的身份信息和凭证信息

Shiro安全框架简介

[users]
admin=1234
tom=222
jack=456
  1. 编写认证代码
public class ShiroAuthentication {
    public static void main(String[] args) {
        // 1.创建SecurityManager安全管理器的实现类
        DefaultSecurityManager securityManager = new DefaultSecurityManager();

        // 2.将Realm中的数据设置到安全管理器中
        securityManager.setRealm(new IniRealm("classpath:shiro.ini")); // Realm去读取ini中的主体与凭证信息

        // 3.将安全管理器设置到全局安全工具类中
        SecurityUtils.setSecurityManager(securityManager);

        // 4.获取主体
        Subject subject = SecurityUtils.getSubject();

        // 5.认证
        if (!subject.isAuthenticated()) { // 是否已经认证
            // 如果没有认证过,那么证明该用户第一次登录.收集使用者的身份信息和凭证信息
            UsernamePasswordToken token = new UsernamePasswordToken("admin","1234"); // 模拟前台输入
            // 提交身份信息和凭证信息
            subject.login(token);
        }
    }
}

3.4 处理结果

如果提交成功,执行后续的逻辑代码;提交过程中出现错误,那么Shiro将以抛异常的形式声明错误

异常列表:

  • UnknowAccountException  --> 未知账号异常【用户名错误】
  • IncorrectCredentialsException  --> 凭证信息异常【密码错误】
  • LockedAccountException  --> 锁定账号异常
  • ExcessiveAttemptsException  --> 过度尝试异常
  • AuthenticationException  --> 身份认证异常

修改以上程序:

try {
    // 开始认证
    subject.login(token);
}catch (UnknownAccountException e){
    System.out.println("账号错误");
}catch (IncorrectCredentialsException e){
    System.out.println("密码错误");
}

3.5 获取主体身份信息与注销

此操作必须保证认证通过

  • 获取主体身份信息
if (subject.isAuthenticated()){ // 认证通过
    Object principal = subject.getPrincipal(); // 6. 获取身份信息
    System.out.println(principal); // admin
}
  • 注销
subject.logout();

3.6 底层实现

身份信息校验

  • 在SimpleAccountRealm类中的doGetAuthenticationInfo方法判断身份信息是否一致
  • 如果用户名错误,那么返回的info==null,系统抛出UnknowAccountException异常

Shiro安全框架简介

Shiro安全框架简介

密码校验

  • 在AuthenticatingRealm类中的assertCredentialsMatch方法进行密码(凭证信息)的校验,Shiro中凭证信息默认的校验规则是equals
  • 如果密码错误,抛出IncorrectCredentialsException异常

Shiro安全框架简介

Shiro安全框架简介

3.7 自定义Realm

以后校验用户名肯定不能使用Shiro中定义的,需要连接数据库,通过用户名查询。所以Shiro中也可以让我们自定义Realm

Realm继承图

全部

Shiro安全框架简介

主要部分

Shiro安全框架简介

认证方法和授权方法都在AuthorizingRealm定义为抽象方法,等待子类继承并重写这两个方法。

SimpleAccountRealm继承了AuthorizingRealm,所以这个类里面有认证和授权功能,其两个功能对应的方法为:

  • doGetAuthenticationInfo:认证
  • doGetAuthorizationInfo:授权

以后自定义的Realm只需要继承AuthorizingRealm,然后重写这两个方法

/**
* 自定义Realm,继承AuthorizingRealm类
*/
public class LoginRealm extends AuthorizingRealm {
    /*
     授权
    */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    /*
     认证
    */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        return null;
    }
}

在doGetAuthenticationInfo方法中获取用户的身份信息,然后校验是否和数据库中的一致

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    // getPrincipal()获取身份信息
    String username = (String) token.getPrincipal();
    if ("admin".equals(username)){
        // 正确 返回AuthenticationInfo的实现类
        // 参数:1.当前用户的身份信息 2.验证主体的凭证信息[如果和前台传入的不一致,抛出IncorrectCredentialsException异常] 3.当前Realm
        return new SimpleAuthenticationInfo(username,"1234",super.getName());
    }
    // 不正确返回一个null, info == null 抛出UnknownAccountException异常
    return null;
}

3.8 加密

测试程序:

public class MD5Test {
    public static void main(String[] args) {
        // 加密
        Md5Hash md5Hash = new Md5Hash("1234");

        // 加密+salt(盐)
        Md5Hash md5Hash1 = new Md5Hash("1234", "f5gy");

        // 加密+salt(盐)+散列次数
        Md5Hash md5Hash2 = new Md5Hash("1234","f5gy",1024);
    }
}

整合认证:

  1. 修改认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    // getPrincipal()获取身份信息
    String username = (String) token.getPrincipal();
    if ("admin".equals(username)){
        // 正确 返回AuthenticationInfo的实现类
        /*
        参数:
            1.当前用户的身份信息
            2.验证主体的凭证信息[如果和前台传入的不一致,抛出IncorrectCredentialsException异常]
            3.盐
            4.当前Realm
        */
        String password = "75b323294effa42ed07f895f37f9a192";
        String salt = "f5gy";
        return new SimpleAuthenticationInfo(username,password, ByteSource.Util.bytes(salt),super.getName());
    }
    // 不正确返回一个null, info == null 抛出UnknownAccountException异常
    return null;
}
  1. 修改密码比较器

Shiro默认实用的是simpleCredentialsMatcher中的doCredentialsMatcher方法,这个方法使用的是equals的方式进行比较密码。

CredentialsMatcher继承图:

Shiro安全框架简介

使用HashedCredentialsMatcher这个类

LoginRealm realm = new LoginRealm();
HashedCredentialsMatcher hash = new HashedCredentialsMatcher();
// 设置算法
hash.setHashAlgorithmName("MD5");
// 设置散列次数
hash.setHashIterations(1024);
// 设置到Realm中
realm.setCredentialsMatcher(hash);

四、Shiro中的授权

4.1 授权中的关键对象

  • Who

表示主体,主题需要系统中的资源

  • What

表示资源,这个资源可以是一个按钮、菜单等。资源又分为资源实例和资源类型

  • How

表示权限/许可,控制主体对资源的访问

4.2 授权方式

  • 基于角色的访问控制(Role-Based Access Control):以角色为中心进行权限控制
  • 基于资源的访问控制(Resource-Based Access Control):以资源为中心进行权限控制

4.3 权限字符串

权限字符串的规则:资源标识符:操作,意思是对哪个资源进行哪些操作。":"是分割符,权限字符串可以使用"*"来表示通配符

4.4 校验角色

  1. 在doGetAuthorizationInfo方法中设置当前主体的角色
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    // 获取主题中的身份信息[用户名]
    String principal = (String) principals.getPrimaryPrincipal();
    // 返回AuthorizationInfo的实现类
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    // 给当前主体添加角色
    info.addRole("admin");
    info.addRole("user");
    return info;
}
  1. 模拟前台测试
if (subject.isAuthenticated()){
    // 校验单个角色
    System.out.println(subject.hasRole("admin")); // 是否有admin角色
    // 校验多个角色
    System.out.println(subject.hasAllRoles(Arrays.asList("admin", "user"))); // 是否同时有admin user角色
    // 校验 多次 角色
    boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "user", "super"));
    for (boolean b : booleans) {
        System.out.println(b);
    }
}

4.5 校验权限字符串

  1. 在doGetAuthorizationInfo方法中设置当前主体的权限字符串
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    // 获取主题中的身份信息[用户名]
    String principal = (String) principals.getPrimaryPrincipal();
    // 返回AuthorizationInfo的实现类
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    // 给当前主体添加权限字符串
    info.addStringPermission("user:update");
    info.addStringPermission("product:select");
    return info;
}
  1. 模拟前台测试

五、整合SpringBoot

5.1 整合思路

Shiro安全框架简介

5.2 环境搭建

  1. 导入springboot和shiro整合的依赖包
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-starter</artifactId>
    <version>1.4.0</version>
</dependency>
  1. 创建一个类,继承AuthorizingRealm类,重写授权和认证方法
public class MyRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        return null;
    }
}
  1. 编写shiro和springboot整合的配置
@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 将SecurityManager设置到Filter中
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        return shiroFilterFactoryBean;
    }
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(Realm realm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        // 给安全管理器设置Realm
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }
    @Bean
    public Realm Realm(){
        LoginRealm loginRealm = new LoginRealm();
        return loginRealm;
    }
}
  1. 在ShiroFilter过滤器中配置需要拦截的资源URL
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    // 将SecurityManager设置到Filter中
    shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
    
    //设置受限资源
    Map<String,String> map = new HashMap<>();
    /**
    * authc:该路径资源需要认证和授权
    */
    map.put("/**","authc"); // "/**"代表所有的资源路径都拦截
    shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
    return shiroFilterFactoryBean;
}
  1. 访问资源跳转到login.jsp,修改默认跳转路径
shiroFilterFactoryBean.setLoginUrl("/doLogin");

@Controller
public class IndexController {

    @GetMapping("/doLogin")
    public String doLogin(){
        return "login";
    }
}
  1. 拦截后访问/doLogin路径

Shiro安全框架简介

5.3 ShiroFilter过滤列表

Shiro中提供了多个默认的过滤器,用这些过滤器来控制指定URL路径下的资源

配置缩写

对应过滤器

描述

anon

AnonymousFilter

指定URL路径下的资源可以匿名访问

authc

FormAuthenticationFilter

指定URL路径下的资源需要认证过后才能访问

authcBasic

BasicHttpAuthenticationFilter

指定URL路径下的资源需要basic登录

logout

LogoutFilter

注销过滤器,只需配置对应的URL路径即可实现

noSessionCreation

NoSessionCreationFilter

禁止创建Session会话

perms

PermissionsAuthorizationFilter

需要有该URL资源对应的权限字符串才能访问

port

PortFilter

指定某个端口可以访问

rest

HttpMethodPermissionFilter

将HTTP请求转换成相对应的动词来构建权限字符串

roles

RolesAuthorizationFilter

需要有指定角色才能访问

ssl

SslFilter

需要https请求才能访问

user

UserFilter

需要已登录或者"记住我"的用户才能访问

六、连接数据库完成认证

6.1 注册

  1. 表设计

Shiro安全框架简介

  1. 注册页面
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Title</title>
  </head>
  <body>
    <h1>注册页面</h1>
    <hr>
    <form action="/user/register" method="post">
      用户名 : <input type="text" name="username"><br>
      密码 : <input type="password" name="password"> <br>
       <input type="submit" value="注册">
    </form>
  </body>
</html>
  1. 实体类POJO
@Data
public class User {
    private Long id;
    private String username;
    private String password;
    private String salt;
}
  1. 编写Controller,调用service处理业务逻辑
@Controller
@RequestMapping("/user")
public class UserController {
    
    @Autowired
    private UserService userService;

    @GetMapping("/doRegister")
    public String doRegister(){
        return "register";
    }
    
    @PostMapping("/register")
    public String register(User user){
    int count = userService.register(user);
        if (count > 0)
            return "redirect:/doLogin";
        else
            return "redirect:/doRegister";
    }
}
  1. 在ShiroFilter放过这些URL路径
map.put("/user/register","anon");
map.put("/user/doRegister","anon");
  1. service中完成加密和散列盐,调用Mapper完成注册
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public int register(User user) {
        String salt = "ga*n";
        Md5Hash md5Hash = new Md5Hash(user.getPassword(),salt,1024);
        user.setPassword(md5Hash.toHex());
        user.setSalt(salt);
        return userMapper.insert(user);
    }
}
  1. Mapper接口和SQL语句
public interface UserMapper {

    int insert(User user);
}
<mapper namespace="com.jiuxiao.mapper.UserMapper">

  <insert id="insert">
    insert into t_user(id,username,password,salt) values(null,#{username},#{password},#{salt})
  </insert>
</mapper>
  1. 启动类上添加@MapperScan注解,扫描Mapper包
@SpringBootApplication
@MapperScan("com.jiuxiao.mapper")
public class ShiroApp {
    public static void main(String[] args) {
        SpringApplication.run(ShiroApp.class,args);
    }
}
  1. 注册页面输入用户信息后,完成注册
  2. Shiro安全框架简介

6.2 认证

  1. 在Shiro配置中修改密码比较器
@Bean
public Realm realm(){
    LoginRealm loginRealm = new LoginRealm();
    HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
    matcher.setHashAlgorithmName("MD5");
    matcher.setHashIterations(1024);
    loginRealm.setCredentialsMatcher(matcher);
    return loginRealm;
}
  1. 在自定义的Realm的doGetAuthenticationInfo方法中编写认证逻辑
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    
    @Autowired
    private UserService userService;
    
    // 获取主体中的身份信息
    String principal = (String) authenticationToken.getPrincipal();
    // 调用service查询数据库
    User user = userService.selectByUsername(principal);
    if (!ObjectUtils.isEmpty(user)){
        // 如果可以查询到,校验密码
        return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),super.getName());
    }
    // 查询不到直接返回null
    return null;
}
  1. service调用Mapper
@Override
public User selectByUsername(String principal) {
    return userMapper.selectByUsername(principal);
}
  1. Mapper接口与SQL语句
User selectByUsername(String principal);
<select id="selectByUsername" resultType="com.jiuxiao.pojo.User">
   select * from t_user where username = #{username}
</select>
  1. 登录页面代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>$Title$</title>
  </head>
  <body>
    <h1>登录页面</h1>
    <hr>
    <form action="/user/login" method="post">
      用户名: <input type="text" name="username"> <br>
      密码: <input type="password" name="password"> <br>
      <input type="submit" value="登录">
    </form>
  </body>
</html>
  1. 在Controller中编写对应URL,封装token,并处理异常结果
@Controller
@RequestMapping("/user")
public class UserController {

    @PostMapping("/login")
    public String login(String username,String password){
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            subject.login(token);
            return "index"; // 登陆成功跳转到首页
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户名错误");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密码错误");
        }
        return "login"; // 登录失败跳转到登录页面
    }
}
  1. 首页代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>$Title$</title>
</head>
<body>
  <h1>首页</h1>
  <hr>
  <ul>
    <li><a href="">用户管理</a></li>
    <li><a href="">商品管理</a></li>
    <li><a href="">菜单管理</a></li>
    <li><a href="">物流管理</a></li>
  </ul>
</body>
</html>
  1. 在ShiroFilter过滤器中放过登录URL
map.put("/user/login","anon");
  1. 页面登录成功后,进入到index.html

Shiro安全框架简介

6.3 注销

① 配置方式

  1. 在ShiroFilter过滤器中添加注销的URL
map.put("/user/logout","logout");
  1. 在页面直接输入这个URL即可注销

② 代码方式

调用subject的logout方法完成注销,页面访问该Controller的URL

七、授权的基本使用

7.1 校验角色

在自定义的Realm的doGetAuthorizationInfo方法中给当前主体赋予角色

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    // 获取主体中的身份信息
    String principal = (String) principalCollection.getPrimaryPrincipal();
    if ("jiuxiao".equals(principal)){ // 给jiuxiao用户赋予admin和user角色
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        // 增加admin和user角色
        simpleAuthorizationInfo.addRole("admin");
        simpleAuthorizationInfo.addRole("user");
        return simpleAuthorizationInfo;
    }
    return null;
}

① 编码方式

单个角色:使用subject中的hasRole方法

@Controller
@RequestMapping("/user")
public class UserController {
    
    @GetMapping("/save")
    @ResponseBody
    public String save(){
        Subject subject = SecurityUtils.getSubject();
        if (!subject.hasRole("admin")) { // 校验当前角色是否有admin这个角色
            return "权限不足";
        }else{
          return "访问成功";   
        }
    }
}

多个角色:使用subject中的hasAllRoles方法【这些角色都有才能访问】

@Controller
@RequestMapping("/user")
public class UserController {
    
    @GetMapping("/save")
    @ResponseBody
    public String save(){
        List<String> roles = Arrays.asList("admin", "user");
        if (SecurityUtils.getSubject().hasAllRoles(roles)) {
            return "访问成功";
        }else{
            return "权限不足";
        }
    }
}

② 注解方式

单个角色:直接在注解参数中写入对应的角色即可

@Controller
@RequestMapping("/user")
public class UserController {
    
    @GetMapping("/save")
    @ResponseBody
    @RequiresRoles("admin")
    public String save(){
        return "访问成功";
    }
}

多个角色:在注解中以数组的形式写入多个角色【这些角色都有才能访问】

@GetMapping("/save")
@ResponseBody
@RequiresRoles(value = {"admin","user"})
public String save(){
    return "访问成功";
}

7.2 校验权限字符串

在自定义的Realm的doGetAuthorizationInfo方法中给当前主体赋予权限字符串

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    String principal = (String) principalCollection.getPrimaryPrincipal();
    System.out.println("执行授权:"+principal);
    if ("jiuxiao".equals(principal)){
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        // 添加权限字符串
        simpleAuthorizationInfo.addStringPermission("user:update");
        simpleAuthorizationInfo.addStringPermission("product:create");
        return simpleAuthorizationInfo;
    }
    return null;
}

① 编码方式

调用subject中的isPermittedAll方法,参数为可变长参数(可以传一个或者多个)【如果是多个,那么这个主体需要拥有参数里面所有的权限字符串才能访问】

@Controller
@RequestMapping("/user")
public class UserController {
    
    @GetMapping("/save")
    @ResponseBody
    public String save(){
        Subject subject = SecurityUtils.getSubject();
        // if (subject.isPermittedAll("user:update")){ // 判断当前主体使用拥有对user资源的001实例的更新操作
        if (subject.isPermittedAll("user:update","product:update")){
            return "访问成功";
        }else{
            return "权限不足";
        }
    }
}

② 注解方式

单个权限字符串:直接在注解参数中写入需要校验的权限字符串即可

@GetMapping("/save")
@ResponseBody
@RequiresPermissions("user:update")
public String save(){
    return "访问成功";
}

多个权限字符串:在注解中以数组的形式写入多个权限字符串【当前主体主要拥有这些权限字符串才能访问】

@GetMapping("/save")
@ResponseBody
@RequiresPermissions(value = {"user:update","product:create"})
public String save(){
    return "访问成功";
}

③ 配置方式

在ShiroFilter过滤器使用perms进行权限的校验

map.put("/user/save","perms[user:update,product:delete]"); // 数组中添加权限字符串

如果权限不足,页面抛出401错误

在ShiroFilter中定义权限不足后跳转的URL

注意:定义权限不足跳转URL的方式只限制配置方式,别的方式都不能使用,权限不足时会抛出AuthorizationException异常

八、连接数据库完成授权

8.1 表设计:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for role_perms
-- ----------------------------
DROP TABLE IF EXISTS `role_perms`;
CREATE TABLE `role_perms`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `roleid` int(11) NULL DEFAULT NULL COMMENT '角色id',
  `permid` int(11) NULL DEFAULT NULL COMMENT '权限id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_croatian_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for role_user
-- ----------------------------
DROP TABLE IF EXISTS `role_user`;
CREATE TABLE `role_user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `roleid` int(11) NULL DEFAULT NULL COMMENT '角色id',
  `userid` int(11) NULL DEFAULT NULL COMMENT '用户id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_croatian_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for t_perms
-- ----------------------------
DROP TABLE IF EXISTS `t_perms`;
CREATE TABLE `t_perms`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `perm` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_croatian_ci NULL DEFAULT NULL COMMENT '权限字符串',
  `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_croatian_ci NULL DEFAULT NULL COMMENT '资源URL',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_croatian_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for t_role
-- ----------------------------
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_croatian_ci NULL DEFAULT NULL COMMENT '角色',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_croatian_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_croatian_ci NULL DEFAULT NULL COMMENT '用户名(身份信息)',
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_croatian_ci NULL DEFAULT NULL COMMENT '密码(凭证信息)',
  `salt` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_croatian_ci NULL DEFAULT NULL COMMENT '盐',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_croatian_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

8.2 POJO

User

@Data
public class User {
    private Long id;
    private String username;
    private String password;
    private String salt;
    private List<Role> roles;
}

Role

@Data
public class Role {
    private Integer id;
    private String name;
    private List<Perms> perms;
}

Perms文章来源地址https://www.toymoban.com/news/detail-419135.html

@Data
public class Perms {
    private Integer id;
    private String perm;
    private String url;
}

8.3 授权角色

  1. Mapper接口与SQL语句
List<Role> selectRoleNameByUserId(String username);
<select id="selectRoleNameByUserId" resultType="com.jiuxiao.pojo.Role">
    select r.id,r.name
    from t_user u
    left join role_user ru on ru.userid = u.id
    left join t_role r on r.id = ru.roleid
    where u.username = #{username}
</select>
  1. Service
@Override
public List<Role> getRoleNameByUsername(String username) {
    return userMapper.selectRoleNameByUsername(username);
}
  1. 自定义Realm中进行授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    String principal = (String) principalCollection.getPrimaryPrincipal();
    List<Role> roles = userService.getRoleNameByUsername(principal);
    if(!CollectionUtils.isEmpty(roles)){
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        roles.forEach(role-> simpleAuthorizationInfo.addRole(role.getName()));
        return simpleAuthorizationInfo;
    }
    return null;
}

8.4 授权字符串

  1. Mapper接口与SQL语句
 List<String> selectPermByRoleId(Integer id);
<select id="selectPermByRoleId" resultType="string">
    select p.perm
    from t_role r
    left join role_perms rp on r.id = rp.roleid
    left join t_perms p on rp.permid = p.id
    where r.id = #{id}
</select>
  1. Service
@Override
public List<String> getPermByRoleId(Integer id) {
    return userMapper.selectPermByRoleId(id);
}
  1. 自定义Realm进行授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    String principal = (String) principalCollection.getPrimaryPrincipal();
    List<Role> roles = userService.getRoleNameByUsername(principal);
    if(!CollectionUtils.isEmpty(roles)){
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        roles.forEach(role-> simpleAuthorizationInfo.addRole(role.getName()));
        roles.forEach(role->{
            List<String> perms = userService.getPermByRoleId(role.getId());
            simpleAuthorizationInfo.addStringPermissions(perms);
        });
        return simpleAuthorizationInfo;
    }
    return null;
}

九、Shiro与thymeleaf整合

9.1 导入依赖

<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>

9.2 配置方言

@Bean
public ShiroDialect shiroDialect(){
    return new ShiroDialect();
}

9.3 引入工作空间

<html lang="en" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

9.4 常用标签使用

<!-- 验证当前用户是否为"访客",即未认证的用户 -->
<p shiro:guest="">未认证</p>

<!-- 认证通过或者已经"记住我"的用户 -->
<p shiro:user="">hello</p>

<!-- 认证通过的用户 -->
<p shiro:authenticated="">hello</p>

<!-- 输出当前用户信息,通常为账号登录信息 -->
<p shiro:principal></p>

<!-- 判断当前用户是否拥有该角色 -->
<p shiro:hasRole="admin">拥有该角色</p>

<!-- 当前用户没有该角色认证通过 -->
<p shiro:lacksRole="user">没有改角色</p>

<!-- 判断当前用户是否拥有以下所有角色 -->
<p shiro:hasAllRoles="admin,user"></p>

<!-- 判断当前用户是否拥有以下任意一个角色 -->
<p shiro:hasAnyRoles="admin,user"></p>

<!-- 判断当前用户是否拥有以下权限字符串 -->
<p shiro:hasPermission="user:add"></p>

<!-- 当前用户没有该权限字符串认证通过 -->
<p shiro:lacksPermission="user:add"></p>

到了这里,关于Shiro安全框架简介的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 安全框架Spring Security是什么?如何理解Spring Security的权限管理?

    大家好,我是卷心菜。本篇主要讲解Spring Security的基本介绍和架构分析,如果您看完文章有所收获,可以三连支持博主哦~,嘻嘻。 🎁 作者简介: 在校大学生一枚,Java领域新星创作者,Java、Python正在学习中,期待和大家一起学习一起进步~ 💗 个人主页:我是一棵卷心菜的

    2024年02月02日
    浏览(55)
  • Shiro框架:Shiro SecurityManager安全管理器解析

    目录 1. SecurityManager介绍 1.1 Authenticator 1.2 Authorizer 1.3 SessionManager 2. DefaultWebSecurityManager解析 2.1 Destroyable 2.2 CacheManagerAware 2.3 EventBusAware 2.4 CachingSecurityManager(聚合缓存管理和事件监听管理功能) 2.5 RealmSecurityManager(聚合Realm管理功能) 2.6 AuthenticatingSecurityManager(聚合登

    2024年01月17日
    浏览(49)
  • 请简单介绍一下Shiro框架是什么?Shiro在Java安全领域的主要作用是什么?Shiro主要提供了哪些安全功能

    会话管理(Session Management):Shiro能够管理用户的会话状态,包括会话的创建、维护和销毁。它支持多种会话存储方式(如内存、数据库等),并提供了会话超时、会话失效等机制,以确保会话的安全性。 跨平台集成:Shiro不仅适用于Web应用程序,还可以轻松集成到桌面应用

    2024年04月13日
    浏览(60)
  • 请简单介绍一下Shiro框架是什么?Shiro在Java安全领域的主要作用是什么?Shiro主要提供了哪些安全功能?

    请简单介绍一下Shiro框架是什么? Shiro框架是一个强大且灵活的开源安全框架,为Java应用程序提供了全面的安全解决方案。它主要用于身份验证、授权、加密和会话管理等功能,可以轻松地集成到任何Java Web应用程序中,并提供了易于理解和使用的API,使开发人员能够快速实

    2024年04月11日
    浏览(58)
  • 【Java】一文带你快速入门Shiro权限框架

    📓推荐网站(不断完善中):个人博客 📌个人主页:个人主页 👉相关专栏:CSDN专栏 🏝立志赚钱,干活想躺,瞎分享的摸鱼工程师一枚 在我们实战开发过程中,对于权限的控制是必不可少的,一个系统中常见的有 普通会员、管理员、超级管理员 等等不同的角色出现。 我们

    2024年02月08日
    浏览(45)
  • shiro框架基本概念介绍

    Shiro 是一个强大灵活的开源安全框架,可以完全处理身份验证、授权、加密和会话管理 身份验证(Authentication):验证用户的身份,确保用户是合法的。 授权(Authorization):控制用户对系统资源的访问权限,限制用户只能访问其被授权的部分。 会话管理(Session Management):

    2024年02月13日
    浏览(35)
  • 盘点国内前端npm CDN替代方案,基本上可以替代unpkg、jsdelivr

    收集一波常见的加速NPM包的CDN,发现有些还是挺好用的,基本上可以替代unpkg、jsdelivr,用来做博客或者网站加载NPM使用还是可以的。 unpkg.com cdn.jsdelivr.net fastly.jsdelivr.net 使用方法 :直接进官网,搜NPM包名使用。 缺点 :有时候不是很稳定,而且国内有些地方没法访问,jsdel

    2023年04月17日
    浏览(68)
  • JAVA安全框架之shiro

    Apache Shiro 是一个功能强大且易于使用的 Java 安全(权限)框架 。Shiro 可以完 成: 认证、授权、加密、会话管理、与 Web 集成、缓存 等。借助 Shiro 您可以快速轻松 地保护任何应用程序——从最小的移动应用程序到最大的 Web 和企业应用程序。 官网 :https://shiro.apache.org/     s

    2024年02月15日
    浏览(52)
  • 利用Unity开发UI并接入HTC VIVE 手柄(基本上写完了,挖个坑12.1开完会更新)

    Unity 2018.3(之前用2021.3,教程比较少,自己复写很困难,就换了个版本)、HTC Vive Pro 2 专业套装、SteamVR 安装Unity资源包:SteamVR Plugin(SDK要求 1.14.15)、vive input unity、icons(可能还有补充) 比较重要的官方文档: 目前发现了两种做法 第①种将全景视频渲染到整个skybox,让天

    2024年02月05日
    浏览(64)
  • SSM 整合 Shiro 安全框架【快速入门】

    更改web路径 创建所需目录 属性名与数据库字段名一 一对应 这里只是为了测试项目,还没有进行编码工作 启动tamcat服务 LoginVo

    2024年02月12日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包