首页 > 其他分享 >(狂神)shiro

(狂神)shiro

时间:2023-02-04 23:33:49浏览次数:57  
标签:用户 shiro apache org 狂神 currentUser Shiro

Ref:https://www.kuangstudy.com/

1、Shiro简介

Shiro 是什么?

Apache Shiro 是 Java 的一个安全(权限)框架。

Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。

Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、缓存等。

下载地址:官网github

功能

  1. Authentication:身份认证/登录,验证用户是不是拥有相应的身份
  2. Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限
  3. Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境,也可以是Web 环境的
  4. Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储
  5. Web Support:Web 支持,可以非常容易的集成到Web 环境
  6. Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率
  7. Concurrency:Shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去
  8. Testing:提供测试支持
  9. Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问
  10. Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了

Shiro架构(外部)

从外部来看Shiro,即从应用程序角度的来观察如何使用Shiro完成工作

  • Subject:应用代码直接交互的对象是Subject,也就是说Shiro的对外API 核心就是Subject。Subject 代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;与Subject 的所有交互都会委托给SecurityManager;Subject 其实是一个门面,SecurityManager才是实际的执行者
  • SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且其管理着所有Subject;可以看出它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC中DispatcherServlet的角色
  • Realm:Shiro从Realm 获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm 得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm 看成DataSource
  • 也就是说对于我们而言,最简单的一个Shiro应用:
    • 应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager;
    • 我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断。

从以上也可以看出,Shiro不提供维护用户/权限,而是通过Realm让开发人员自己注入

Shiro架构(内部)

  1. Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;
  2. SecurityManager:相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。
  3. Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
  4. Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
  5. Realm:可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC实现,也可以是LDAP实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的Realm;
  6. SessionManager:如果写过Servlet就应该知道Session的概念,Session呢需要有人去管理它的生命周期,这个组件就是SessionManager;而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境、EJB等环境;所有呢,Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据;这样的话,比如我们在Web环境用,刚开始是一台Web服务器;接着又上了台EJB服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到Memcached服务器);
  7. SessionDAO:DAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可以实现自己的SessionDAO,通过如JDBC写到数据库;比如想把Session放到Memcached中,可以实现自己的Memcached SessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;
  8. CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
  9. Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的

认证流程

用户 提交 身份信息、凭证信息 封装成 令牌 交由 安全管理器 认证

2、快速入门

拷贝案例

  1. 按照官网提示找到快速入门案例GitHub,从GitHub 的文件中可以看出这个快速入门案例是一个 Maven 项目

  2. 新建一个 Maven 工程,删除其 src 目录,将其作为父工程

  3. 在父工程中新建一个 Maven 模块

  4. 复制快速入门案例 POM.xml 文件中的依赖 (版本号自选)

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.8.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.slf4j/jcl-over-slf4j -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>2.0.0-alpha5</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>2.0.0-alpha5</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>
    
    
  5. 把快速入门案例中的 resource 下的log4j.properties 复制下来

    log4j.rootLogger=INFO, stdout
    
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
    
    # General Apache libraries
    log4j.logger.org.apache=WARN
    
    # Spring
    log4j.logger.org.springframework=WARN
    
    # Default Shiro logging
    log4j.logger.org.apache.shiro=INFO
    
    # Disable verbose logging
    log4j.logger.org.apache.shiro.util.ThreadContext=WARN
    log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
    
  6. 复制一下 shiro.ini 文件

    需要下载ini插件,如果在setting中无法下载,就去官网下载对应版本的然后导入

    [users]
    # user 'root' with password 'secret' and the 'admin' role
    root = secret, admin
    # user 'guest' with the password 'guest' and the 'guest' role
    guest = guest, guest
    # user 'presidentskroob' with password '12345' ("That's the same combination on
    # my luggage!!!" ;)), and role 'president'
    presidentskroob = 12345, president
    # user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
    darkhelmet = ludicrousspeed, darklord, schwartz
    # user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
    lonestarr = vespa, goodguy, schwartz
    
    # -----------------------------------------------------------------------------
    # Roles with assigned permissions
    #
    # Each line conforms to the format defined in the
    # org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
    # -----------------------------------------------------------------------------
    [roles]
    # 'admin' role has all permissions, indicated by the wildcard '*'
    admin = *
    # The 'schwartz' role can do anything (*) with any lightsaber:
    schwartz = lightsaber:*
    # The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
    # license plate 'eagle5' (instance specific id)
    goodguy = winnebago:drive:eagle5
    
  7. 复制一下 Quickstart.java 文件,如果有导包的错误,把那两个错误的包删掉,就会自动导对的包了,快速入门案例中用的方法过时了

    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.realm.text.IniRealm;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class Quickstart {
         //导入日志
        private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
    
        public static void main(String[] args) {
    
            /*
            得到一个defaultSecurityManager对象
            得到一个绑定了资源目录下shiro.ini文件的iniRealm对象
            通过defaultSecurityManager.setRealm()方法将iniRealm对象设置为单例
            */
            DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
            IniRealm iniRealm=new IniRealm("classpath:shiro.ini");
            defaultSecurityManager.setRealm(iniRealm);
            SecurityUtils.setSecurityManager(defaultSecurityManager);
    
            //获取当前用户
            Subject currentUser = SecurityUtils.getSubject();
    
           //获取当前用户的session,和赋值
            Session session = currentUser.getSession();
            session.setAttribute("someKey", "aValue");
            String value = (String) session.getAttribute("someKey");
            if (value.equals("aValue")) {
                log.info("Retrieved the correct value! [" + value + "]");
            }
    
          //判断当前用户是否有认证
            //这里面的参数都是在ini文件夹中设定的
            if (!currentUser.isAuthenticated()) {
                UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");//得到一个名字为lonestarr(自己设定),内容为密码vespa的令牌
                token.setRememberMe(true);//设置记住我
                try {
                    currentUser.login(token);//通过令牌登录
                } catch (UnknownAccountException uae) {//没有认证
                    log.info("There is no user with username of " + token.getPrincipal());
                } catch (IncorrectCredentialsException ice) {//密码错误
                    log.info("Password for account " + token.getPrincipal() + " was incorrect!");
                } catch (LockedAccountException lae) {
                    log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                            "Please contact your administrator to unlock it.");
                }
    			//其他错误
                catch (AuthenticationException ae) {
                    //unexpected condition?  error?
                }
            }
    
            //say who they are:
            //print their identifying principal (in this case, a username):
            log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
    
            //如果有schwartz这个权限(角色)
            if (currentUser.hasRole("schwartz")) {
                log.info("May the Schwartz be with you!");
            } else {
                log.info("Hello, mere mortal.");
            }
    
            //如果有schwartz这个权限(角色)
            if (currentUser.isPermitted("lightsaber:wield")) {
                log.info("You may use a lightsaber ring.  Use it wisely.");
            } else {
                log.info("Sorry, lightsaber rings are for schwartz masters only.");
            }
    
            //a (very powerful) Instance Level permission:
            if (currentUser.isPermitted("winnebago:drive:eagle5")) {
                log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
                        "Here are the keys - have fun!");
            } else {
                log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
            }
    
            //all done - log out!
            currentUser.logout();
    
            System.exit(0);
        }
    }
    
  8. 运行 Quickstart.java,得到结果

分析案例

1、通过 SecurityUtils 获取当前执行的用户 Subject

Subject currentUser = SecurityUtils.getSubject();

2、通过 当前用户拿到 Session

Session session = currentUser.getSession();

3、用 Session 存值取值

session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");

4、判断用户是否被认证

currentUser.isAuthenticated()

5、执行登录操作

currentUser.login(token);

6 、打印其标识主体

currentUser.getPrincipal()

7、注销

currentUser.logout();

总览

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Quickstart {

    private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);

    public static void main(String[] args) {
        // 工厂模式,通过shiro.ini 配置文件中的信息,生成一个工厂实例
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        // 通过SecurityUtils 获取当前执行的用户Subject:
        Subject currentUser = SecurityUtils.getSubject();

        //通过当前用户拿到Shiro的Session 可以脱离web存值取值
        Session session = currentUser.getSession();
        // 使用session存值取值
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");
        if (value.equals("aValue")) {
            log.info("Retrieved the correct value! [" + value + "]");
        }

        // 让我们登录当前用户,以便我们可以检查角色和权限:
        // 这里和SpringSecurity 使用了类似的代码,判断用户是否被认证
        if (!currentUser.isAuthenticated()) {
            //如果被认证,就可以获得一个令牌(token )
            //通过用户的账号密码生成一个令牌
            UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
            //设置记住我功能
            token.setRememberMe(true);
            try {
                //执行登录操作
                currentUser.login(token);
            } catch (UnknownAccountException uae) {
                //如果用户名不存在
                log.info("There is no user with username of " + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) {
                // 如果密码不正确
                log.info("Password for account " + token.getPrincipal() + " was incorrect!");
            } catch (LockedAccountException lae) {
                //用户被锁定,如密码输出过多,则被锁住
                log.info("The account for username " + token.getPrincipal() + " is locked.  " +"Please contact your administrator to unlock it.");
            }
            // ... 在此处捕获更多异常
            catch (AuthenticationException ae) {
                //意外情况?错误?
            }
        }

        //currentUser些用法
        //打印其标识主体(在这种情况下,为用户名) :
        log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

        // 检查角色是否存在
        if (currentUser.hasRole("schwartz")) {
            log.info("May the Schwartz be with you!");
        } else {
            log.info("Hello, mere mortal.");
        }

        //粗粒度
        if (currentUser.isPermitted("lightsaber:wield")) {
            log.info("You may use a lightsaber ring.  Use it wisely.");
        } else {
            log.info("Sorry, lightsaber rings are for schwartz masters only.");
        }

        //细粒度
        if (currentUser.isPermitted("winnebago:drive:eagle5")) {
            log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'." +"Here are the keys - have fun!");
        } else {
            log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
        }

        //注销
        currentUser.logout();

        //结束
        System.exit(0);
    }
}

3、环境搭建

编写配置文件

1.在刚刚的父项目中新建一个 springboot 模块

2.导入 SpringBoot 和 Shiro 整合包的依赖

<!--SpringBoot 和 Shiro 整合包-->
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring-boot-web-starter -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.8.0</version>
</dependency>
<!--
    下面是编写配置文件
    Shiro 三大要素
    subject -> ShiroFilterFactoryBean
    securityManager -> DefaultWebSecurityManager
    realm
    实际操作中对象创建的顺序 : realm -> securityManager -> subject
-->

3.编写自定义的 realm ,需要继承 AuthorizingRealm

//自定义的 Realm
public class UserRealm extends AuthorizingRealm {
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //打印一个提示
        System.out.println("执行了授权方法");
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //打印一个提示
        System.out.println("执行了认证方法");
        return null;
    }
}

4.新建一个 ShiroConfig配置文件

​ 1.创建 Realm

//3. realm
//让 spring 托管自定义的 realm 类
@Bean
public UserRealm userRealm(){
    return new UserRealm();
}

​ 2.创建 securityManager

//2. securityManager -> DefaultWebSecurityManager
// @Qualifier("userRealm") 指定 Bean 的名字为 userRealm
// spring 默认的 BeanName 就是方法名
// name 属性 指定 BeanName
@Bean(name = "SecurityManager")
public DefaultWebSecurityManager getDefaultWebSecurity(@Qualifier("userRealm") UserRealm userRealm){
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    //需要关联自定义的 Realm,通过参数把 Realm 对象传递过来
    securityManager.setRealm(userRealm);
    return securityManager;
}

​ 3.创建 subject

//1. subject -> ShiroFilterFactoryBean
// @Qualifier("securityManager") 指定 Bean 的名字为 securityManager
@Bean(name = "shiroFilterFactoryBean")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager securityManager){
    ShiroFilterFactoryBean subject = new ShiroFilterFactoryBean();
    //设置安全管理器
    //需要关联 securityManager ,通过参数把 securityManager 对象传递过来
    subject.setSecurityManager(securityManager);
    return subject;
}

搭建简单测试环境

1.新建首页

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<div>
    <h1>首页</h1>
    <p th:text="${msg}"></p>

    <hr>
    <a th:href="@{/user/add}">add</a>   | <a th:href="@{/user/update}">update</a>
</div>
</body>
</html>

2.新建增加用户页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>add</h1>
</body>
</html>

3.新建修改用户页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>update</h1>
</body>
</html>

4.编写对应的 Controller

@Controller
public class MyController {

    @RequestMapping({"/","/index"})
    public String toIndex(Model model) {
        model.addAttribute("msg","hello,Shiro");
        return "index";
    }
    
    @RequestMapping("/user/add")
    public String add() {
        return "user/add";
    }

    @RequestMapping("/user/update")
    public String update() {
        return "user/update";
	}
}

5.运行结果

4、使用Shiro实现功能

Shiro实现登录拦截

1.在ShiroConfig中的getShiroFilterFactoryBean方法中添加配置

//添加 Shiro 的内置过滤器
/*
    anon : 无需认证,就可以访问
    authc : 必须认证,才能访问
    user : 必须拥有 “记住我”功能才能用
    perms : 拥有对某个资源的权限才能访问
    role : 拥有某个角色权限才能访问
 */
// 拦截
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/user/add","authc");
filterMap.put("/user/update","authc");
bean.setFilterChainDefinitionMap(filterMap);

2.点击首页的add或者update之后

3.添加拦截成功页面

​ 1.登录页面login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<h1>登录</h1>
<hr>

<form action="">
    <p>用户名:<input type="text" name="username"></p>
    <p>密码:<input type="text" name="password"></p>
    <p>密码:<input type="submit"></p>
</form>
</body>
</html>

​ 2.增加登录接口

@RequestMapping("/toLogin")
public String toLogin() {
    return "login";
}

​ 3.在ShiroConfig中的getShiroFilterFactoryBean方法中添加配置

//设置登录的请求
bean.setLoginUrl("/toLogin");

4.拦截成功页面

Shiro实现用户认证

1.在MyController中编写用户提交表单之后处理

@RequestMapping("/login")
public String login(String username, String password, Model model) {
    //获取一个用户
    Subject subject = SecurityUtils.getSubject();
    // 封装用户的登录数据
    UsernamePasswordToken token = new UsernamePasswordToken(username, password);

    try {
        subject.login(token);//执行登录的方法,如果没有异常就说明ok了
        return "index";
    } catch (UnknownAccountException e) {//用户名不存在
        model.addAttribute("msg","用户名错误");
        return "login";
    } catch (IncorrectCredentialsException e) {//密码不存在
        model.addAttribute("msg","密码错误");
        return "login";
    }

}

2.login.html的修改

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<h1>登录</h1>
<hr>

<p th:text="${msg}" style="color: red;"></p>
<form th:action="@{/login}">
    <p>用户名:<input type="text" name="username"></p>
    <p>密码:<input type="text" name="password"></p>
    <p>密码:<input type="submit"></p>
</form>
</body>
</html>

3.用户认证编写UserRealm中的认证(doGetAuthenticationInfo)

//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    System.out.println("执行了=>认证doGetAuthorizationInfo");
    // 用户名、密码, 数据中取
    String name = "root";
    String password = "123456";

    UsernamePasswordToken userToken = (UsernamePasswordToken) token;

    if (!userToken.getUsername().equals(name)) {
        return null;//抛出异常 UnknownAccountException
    }

    // 密码认证,shiro做
    return new SimpleAuthenticationInfo("",password,"");
}

Shiro实现用户注销

在控制器中添加一个退出登录的方法

//退出登录
@RequestMapping("/logout")
public String logout(){
    Subject subject = SecurityUtils.getSubject();
    subject.logout();
    return "login";
}

5、整合Mybatis

1、导入依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

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

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.8</version>
</dependency>
<!--引入mybatis,这是MyBatis官方提供的适配spring Boot的,而不是spring Boot自己的-->
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>

2、配置文件application.yml的编写

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

# 指定myBatis的核心配置文件与Mapper映射文件
# 注意:对应实体类的路径
mybatis:
  mapper-locations: classpath:mybatis/mapper/*.xml
  type-aliases-package: com.study.pojo
  configuration:
    map-underscore-to-camel-case: true

3、User类的编写

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}

4、UserMapper.xml映射

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study.mapper.UserMapper">
    <select id="queryUserByName" parameterType="String" resultType="User">
        select *
        from user where name=#{name};
    </select>
</mapper>

5、UserService接口实现

public interface UserService {
    public User queryUserByName(String name);
}

6、UserServiceImpl业务逻辑

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;
    @Override
    public User queryUserByName(String name) {
        return userMapper.queryUserByName(name);
    }
}

7、测试环境

@SpringBootTest
class ShiroSpringbootApplicationTests {
    @Autowired
    UserService userService;
    
    @Test
    void contextLoads() {
        System.out.println(userService.queryUserByName("root"));
    }
}

8、UserRealm连接真实数据库

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    System.out.println("执行了=>认证doGetAuthorizationInfo");

    UsernamePasswordToken userToken = (UsernamePasswordToken) token;
    // 连接真实数据库取出用户名、密码
    User user = userService.queryUserByName(userToken.getUsername());
    if (user == null) { // 没有这个人
        return null;// 抛出异常 UnknownAccountException
    }
    // 可以加密MD5、MD5盐值加密
    // 密码认证,shiro做
    return new SimpleAuthenticationInfo("",user.getPwd(),"");
}

6、授权及整合Thymeleaf

1、授权

添加认证代码

ShiroConfig中的getShiroFilterFactoryBean方法

//授权,正常情况下,没有授权会跳转到为授权页面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");

添加未授权页面

1.MyController

@RequestMapping("/noauto")
@ResponseBody
public String unauthorized() {
    return "未经授权,无法访问此页面";
}

2.ShiroConfig中的getShiroFilterFactoryBean方法中添加

//未授权页面
bean.setUnauthorizedUrl("/noauto");

实现用户授权

//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    //打印一个提示
    System.out.println("执行了授权方法");
    // 授权
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    // 拿到当前登录的对象
    Subject subject = SecurityUtils.getSubject();
    // 拿到User对象
    User currentUser = (User) subject.getPrincipal();
    // 设置当前用户权限
    info.addStringPermission(currentUser.getPerms());
    return info;
}

2、整合Thymeleaf

导入依赖

<!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.1.0</version>
</dependency>

整合ShiroDialect

// 整合ShiroDialect:用来整合shiro-thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
    return new ShiroDialect();
}

index.html页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <h1>首页</h1>
    <p th:text="${msg}"></p>
    <div shiro:notAuthenticated>
        <a th:href="@{/toLogin}">登录</a>
    </div>
    <div shiro:authenticated>
        <a th:href="@{/logout}">logout</a>
    </div>
    <hr>
    <div shiro:hasPermission="user:add">
        <a th:href="@{/user/add}">add</a>
    </div>
    <div shiro:hasPermission="user:update">
        <a th:href="@{/user/update}">update</a>
    </div>
</body>
</html>

标签:用户,shiro,apache,org,狂神,currentUser,Shiro
From: https://www.cnblogs.com/mountainstudy/p/17092635.html

相关文章

  • 狂神说Springboot笔记
    狂神说SpringBoot视频链接:B站教学视频SpringBoot系列笔记:狂神说SpringBoot01:Hello,World!狂神说SpringBoot02:运行原理初探狂神说SpringBoot03:yaml配置注入狂神......
  • (狂神)Reids
    Ref:https://www.kuangstudy.com/1、Nosql概述为什么要用Nosql我们生活在大数据时代,一般的数据库无法进行分析处理了!1、单机Mysql的时代!90年代,一个基本的网......
  • SpringBoot框架下shiro与jwt的结合的用户登录
    写了一次使用shiro和jwt的用户登录(没有涉及到用户权限的控制),下面进行很简单的技术总结之前使用的是Redis中保存用户信息,使用uuid-用户信息的键值对来判断用户......
  • Shiro配置类
    importorg.apache.shiro.authc.credential.HashedCredentialsMatcher;importorg.apache.shiro.mgt.SecurityManager;importorg.apache.shiro.spring.web.ShiroFilter......
  • 狂神说笔记——Linux快速入门27
    Linux快速入门参考于:B站狂神视频!Java开发之路:JavaSE、MySQL、前端(HTML、Css、JS)、JavaWeb、SSM框架、SpringBoot、Vue、SpringCloud、Mybatis-plus、Git、Linux(CentO......
  • 狂神说笔记——Nginx快速入门28
    Nginx快速入门在低并发的情况下,一个jar包启动应用就够了,然后内部tomcat返回内容给用户。随着用户越来越多了,并发量慢慢增大了,此时一台服务器满足不了需求了。于......
  • (狂神)MyBatis-Plus
    Ref:https://www.kuangstudy.com/1、MyBatisPlus概述需要的基础:把我的MyBatis、Spring、SpringMVC就可以学习这个了!为什么要学习它呢?MyBatisPlus可以节省我们大量工......
  • (狂神) Git版本控制
    1、版本控制1.1什么是版本控制版本控制(Revisioncontrol)是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史,方便查看更改历史记录,备份以便恢复以前的......
  • Shiro+SpringBoot+Mybatis+Redis实现权限系统
     这个项目涉及到的技术:SpringBoot,Thymeleaf,MyBaits,Redis,Shiro,JavaMail,EasyUI,Excel导入、导出 下面展示一下界面: 主页:  用户列表:  角色权限授予: 资源列表:  用户角......
  • shiro登录认证过程讲解
    先粘出登录的代码​@RequestMapping(value="/submitLogin",method=RequestMethod.POST)@ResponseBodypublicMap<String,Object>submitLogin(Stringusername,String......