Shiro安全服务

Shiro#

为什么要⽤shiro:
1.项⽬中的密码是否可以明⽂存储?
2.是否任意访客,⽆论是否登录都可以访问任何功能?
3.项⽬中的各种功能操作,是否是所有⽤户都可以随意使⽤?
综上,当项⽬中的某些功能被使⽤时,需要进⾏安全校验,进⽽保证整个系统的运⾏秩序。

Shiro是什么#

  • Apache Shiro 是 Java 的⼀个安全(权限)框架。
    Shiro 可以轻松的完成:身份认证、授权、加密、会话管理等功能
  • Shiro 可以⾮常容易的开发出⾜够好的应⽤,其不仅可以⽤在JavaSE 环境,也可以⽤在 JavaEE 环境。
    功能强⼤且易⽤,可以快速轻松地保护任何应⽤程序 ( 从最⼩的移动应⽤程序到最⼤的Web和企业应⽤程序。)
  • ⽅便的与Web 集成和搭建缓存。
  • 下载:http://shiro.apache.org/

功能简介#

基本功能点如下图所示:

image-20200824090046430

  • Authentication:身份认证/登录,验证⽤户是不是拥有相应的身份;
  • Authorization:授权,即权限验证,验证某个已认证的⽤户是否拥有某个权限;即判断⽤户是否能进⾏什么操作。如:验证某个⽤户是否拥有某个⻆⾊。或者细粒度的验证某个⽤户对某个资源是否具有某个权限;
  • Session Manager:会话管理,即⽤户登录后就是⼀次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境,也可以是 Web 环境的;
  • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,⽽不是明⽂存储;
  • Web Support:Web ⽀持,可以⾮常容易的集成到Web 环境;
  • Caching:缓存,⽐如⽤户登录后,其⽤户信息、拥有的⻆⾊/权限不必每次去查,这样可以提⾼效率;
  • Remember Me:记住我,这个是⾮常常⻅的功能,即⼀次登录后,下次再来的话可以⽴即知道你是哪个⽤户

High-Level Overview 高级概述#

在概念层,Shiro 架构包含三个主要的理念:Subject,SecurityManager和 Realm。下面的图展示了这些组件如何相互作用,我们将在下面依次对其进行描述。

img

  • Subject:当前用户,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它–当前和软件交互的任何事件。
  • SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞。
  • Realms:用于进行权限信息的验证,我们自己实现。Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。

我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。

Shiro 认证过程#

img

整合SpringBoot#

依赖#


    <dependencies>

        <!--   log4j     -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <!-- shiro-spring 整合包 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.5.3</version>
        </dependency>
    </dependencies>

  

登录页面控制#

package com.example.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {


    //判断登录是否成功
    @RequestMapping("/login")
    public String index4(String username, String pwd, Model model){
        //获取当前用户
        Subject currentUser = SecurityUtils.getSubject();
        //将前端的账号和密码封装成密码令牌
        UsernamePasswordToken token = new UsernamePasswordToken(username,pwd);

        //登录验证
        // let's login the current user so we can check against roles and permissions:
        try {
            //自动判断登录 ,调用 realm对象的doGetAuthenticationInfo方法
                currentUser.login(token);
                return "index";
        } catch (UnknownAccountException uae) {
                model.addAttribute("msg","账号错误");
                return "login";
        } catch (IncorrectCredentialsException ice) {
                 model.addAttribute("msg","密码错误");
                return "login";
        }

    }
	
 
}

设置shrio配置文件#

package com.example.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.mgt.DefaultSecurityManager;
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.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {


    //三大对象  ShiroFilterFactoryBean,过滤器
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultSecurityManager defaultSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultSecurityManager);

        Map<String, String> map = new LinkedHashMap<>();

        //设置权限访问
        map.put("/user/add","perms[user:add]");
        map.put("/user/update","perms[user:update]");
        map.put("/user/add1","perms[user:add1]");
        
        //设置登录拦截
        //shiro的内置过滤器
        /*
            anon:不需要认证就可以访问
            authc:需要认证
            user:必须要 记住我 功能
            perms:拥有对某个资源的权限才能访问
            role:拥有对某个角色权限才能访问
         */
        //该行代码必须放在授权的代码下,否则授权的拦截无效!
        map.put("/user/**","authc");


        //设置登录url
        bean.setLoginUrl("/toLogin");
        //设置未授权页面
        bean.setUnauthorizedUrl("/noperms");

        //设置过滤链map
        bean.setFilterChainDefinitionMap(map);

        return bean;
    }


    //DefaultSecurityManager对象
    @Bean
    public DefaultSecurityManager DefaultSecurityManager(UserShrio userShrio){

        DefaultSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联realm
        securityManager.setRealm(userShrio);
        return securityManager;
    }

    //realm对象
    @Bean
    public UserShrio userShrio(){

        return new UserShrio();
    }

    @Bean
    public ShiroDialect shiroDialect(){

        return new ShiroDialect();
    }


}

配置shiro 认证和权限拦截#

登录拦截(用户认证)#

  • 先调用
   currentUser.login(token);  //启动自动登录判断  
  • 根据shrio配置文件ShiroConfig中的过滤器配置的 d登录 的拦截
  • 去调用shiro对象的doGetAuthenticationInfo

权限认证#

  • 根据shrio配置文件ShiroConfig中的过滤器配置的 权限授权 的拦截

  • 去调用shiro对象的doGetAuthorizationInfo

shiro真正拦截的地方#

package com.example.config;


import com.example.pojo.User;
import com.example.service.UserService;
import org.apache.shiro.SecurityUtils;
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 org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;


public class UserShrio  extends AuthorizingRealm {

    @Autowired
    private UserService userService;
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("授权---doGetAuthorizationInfo");

        //拿到当前对象
        Subject subject = SecurityUtils.getSubject();
        //取出在认证传递的User对象
        User user = (User) subject.getPrincipal();
        System.out.println("认证的对象---------"+user);

        //给用户添加上相应的权限(来自数据库中的perms字段)
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermission(user.getPerm());


        return info;
    }
    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("认证---doGetAuthenticationInfo");

        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

        //从数据库查询账号密码
        User user = userService.queryByUserName(token.getUsername());
        //如果密码不正确
        if (user==null){
            //会自定抛出错误UnknownAccountException
            return null;
        }
        //SimpleAuthenticationInfo:(参数)三个 或者四个
        /*
          用户名–此处传的是用户对象  直接通过token.getPrincipal()方法获取—获取当前记录的用户名,从这个用户名获取一系列的对应需求属性。
         这里传入user 在上面授权中就可以使用
       userInfo.getPassword(), //密码—从数据库中获取的密码
        salt, 盐–用于加密密码对比
        getName() //当前的realm名
         */
        //userInfo, //

        //密码认证 ,shiro自己做
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPwd(),"");
        return info;
    }
}

整合thymeleaf#

依赖#

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

使用#

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <h1>index</h1>
    	<!--//根据不同的权限判断是否显示-->
    <div shiro:hasPermission="user:add">
        <a th:href="@{/user/add}">add</a>
    </div>

    <br>
    <div shiro:hasPermission="user:update">
        <a th:href="@{/user/update}">update</a> <br>
    </div>

    <br>
    <a th:href="@{/user/add1}">add</a>

</body>
</html>

作者:Esofar

出处:https://www.cnblogs.com/firsthelloworld/p/13554535.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   我不想学编丿程  阅读(179)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示