SpringBoot———安全框架之Shiro



一、简介

  • Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

与SpringSecurity的区别

  • shiro:灵活性强,易学易扩展。同时,不仅可以在web中使用,可以工作在任务环境内中。
  • security:灵活性较差,但与spring整合性好。
  • 应用环境:不依赖于任何框架,可在JavaSE中使用,更常见的是JavaEE中。
  • 功能:管理应用程序中与安全相关的全部,同时尽可能支持多种实现方法。Shiro是建立在完善的接口驱动设计和面向对象原则之上的,支持各种自定义行为。

二、功能

在这里插入图片描述
Shiro不会去维护用户、维护权限,需要自己设计,通过相应的接口注入给Shiro。

四大基石----身份验证,授权,会话管理,加密

  1. Authentication:用户认证,验证用户是不是拥有相应的身份。
  2. Authorization:用户授权,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情。
  3. Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中。
  4. Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储。

Shiro扩展功能

  1. Web Support:Web支持,可以非常容易的集成到Web环境。

  2. Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率。

  3. Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去。

  4. Testing:提供测试支持。

  5. Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。

  6. Remember Me:记住我。

Shiro尝试在任何应用环境下实现这些功能,而不依赖其他框架、容器或应用服务器。

三、架构

在这里插入图片描述
Shiro 有三大核心组件,即 Subject、SecurityManager 和 Realm。

Subject——主体

  • 外部应用于subject进行交互,subject记录了当前的操作方————记为主体。可以是用户或者网络爬虫。
  • 作用:主体需要访问系统,系统则对其进行认证和授权
  • 结构:包含Principals 和 Credentials 两个信息

Principals:代表身份。可 以是用户名、邮件、手机号码等等,用来标识一个登录主体的身份。
Credentials:代表凭证。常见的有密码,数字证书等等。

外部程序通过subject(用户)进行认证授权,而subject是通过SecurityManager安全管理器进行认证授权,即:

Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。

SecurityManager——安全管理器

  • Shiro框架的核心。典型的Facade模式
  • 作用:Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务(认证和授权)。
  • 结构:SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager三个接口。

原理:SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。

开发人员将大部分精力放在了 Subject 认证主体上,与 Subject 交互背后的安全操作,则由 SecurityManager 来完成。

Authenricator——认证器
  • 作用:对Subject进行认证。Subject的信息在shrio中是通过AuthenticationToken对象来储存,由AuthenricationStrategy进行验证管理。
  • Authenticator是一个接口,通过ModularRealmAuthenticator实现类基本上可以满足大多数需求,也可以自定义认证器。
Authorizer——授权器
  • 作用:Subject认证后,授权器来对其授予对应角色权限。即控制着用户能访问应用中的哪些功能。
SessionManager——会话管理器
  • 作用:Shiro框架定义了一套会话管理,它不依赖web容器的session
  • 优点:Shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。
    可以理解为:Shiro抽象了一个自己的Session来管理主体与应用之间交互的数据。
SessionDao——会话DAO
  • Session的接口,Shiro通过它来管理session数据。
  • 作用:个性化的session数据储存需要使用sessionDao。
CacheManager——缓存控制器
  • 作用:主要对Session数据和用户权限数据进行缓存,减小数据库的访问压力。

Realm——领域

  • 相当于DataSource数据源。
  • 作用:securityManager进行安全认证需要通过Realm获取用户权限数据。比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。
  • 原理:需继承AuthorizingRealm接口。至少有一个——用于认证和授权,也可自定义多个。

Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。

  • 例如:当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。

Shiro不知道用户或权限存储在哪及以何种格式存储,所以一般在应用中都需要实现自己的Realm密码模块

注意:Subject进行认证和授权都需要调用realm,所以realm不仅仅相当于数据源,还包含了认证和授权的一种逻辑。

  • 实质:Realm是一个安全相关的DAO,封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。

Cryptography——密码管理器

  • 作用:提供了一套加密/解密的组件。比如常用的散列,加/解密等功能。

日常练习所使用的md5算法其实是一种散列算法,只能加密,不能解密。

总结:Shrio的核心部分还是认证和授权部分,其他都是围绕这俩部分进行的。

四、认证流程

  1. 应用程序代码调用 Subject.login(token) 方法,传入代表最终用户身份的 AuthenticationToken 实例 Token。
  2. 将 Subject 实例委托给应用程序的 SecurityManager,开始真正的认证工作。
  3. SecurityManager 根据注入的 Realm ,让SecurityManager得到合法的用户和权限进行判断和安全认证。(Realm可自定义)

五、实现步骤

案例一:搭建环境,进行测试

  1. 创建SpringBoot项目,添加web框架,导入thymeleaf、Shiro依赖。
<!--thymeleaf-->
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<!--Shiro-Spring-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.6.0</version>
</dependency>
  1. 在templates下创建index首页,编写controller,进行测试。
@Controller
public class testController {

    @RequestMapping("/index")
    public String toIndex(Model model){
        model.addAttribute("msg","这是一个Shiro的测试案例");
        return "index";
    }
}
  1. config包下自定义UserRealm
package com.zy.Config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

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;
    }
}
  1. 编写ShrioConfiguration配置类

作用:装配ShiroFilterFactoryBean,DefaultWebSecurityManager和自定义的UserRealm

package com.zy.Config;

import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShrioConfiguration {

    // 装配ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager ){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        return shiroFilterFactoryBean;
    }

    // 装配SecurityManager,注入自定义的UserRealm
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        // 关联自定义的UserRealm
        defaultWebSecurityManager.setRealm(userRealm);
        return defaultWebSecurityManager;
    }
    // 装配Realm
    @Bean(name = "userRealm")
    public UserRealm getUserRealm(){
        return new UserRealm();
    }
}
  1. 配置controller
@Controller
public class testController {
    @RequestMapping("/user/add")
    public String add(){
        return "/user/add";
    }
    @RequestMapping("/user/update")
    public String update(){
        return "/user/update";
    }
    @RequestMapping("/user/show")
    public String show(){
        return "/user/show";
    }
}
  1. 编写前端网页,进行测试
    在这里插入图片描述

index.html

<body th:align="center">
<h1>首页</h1>
<h2 th:text="${msg}"></h2>
<hr>
<a th:href="@{/user/show}">展示页面</a> |
<a th:href="@{/user/add}">添加页面</a> |
<a th:href="@{/user/update}">修改页面</a>
</body>

add.html

<body align="center">
<h2>add页面</h2>
</body>

update.html

<body align="center">
<h2>update页面</h2>
</body>

show.html

<body align="center">
<h2>show页面</h2>
</body>

总结:三个网页均可进入。

案例二:添加登录拦截权限

  1. 在配置类的ShiroFilterFactoryBean中添加权限拦截功能

拦截的权限有:

  • anon:无需认证就可以访问
  • authc:必须认证了才能访问
  • user:必须拥有记住我功能才能使用
  • perms:拥有对某个资源的权限才能访问
  • role:拥有某个角色权限才能访问
 	@Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager ){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        // 添加Shiro内置过滤器
        HashMap<String, String> map = new HashMap<>();
        map.put("/user/show","anon"); // 不用认证
        map.put("/user/add","authc");
        map.put("/user/update","authc");
        // 设置拦截权限
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        // 拦截后跳转到登录页面
        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        return shiroFilterFactoryBean;
    }
  1. 自定义登录页面,配置controller

login.html

<body th:align="center">
<h1>登录</h1>
<hr>
<form method="post" action="/login">
    用户名:<input type="text" name="username">
    密码:<input type="password" name="pwd">
    <input type="submit" value="提交">
</form>
</body>
    @RequestMapping("/toLogin")
    public String toLogin(){
        return "login";
    }

总结:show网页不用登录即可查看,add、update网页会进入登录网页。

案例三:实现用户认证功能

用户认证步骤:

    1. 获取当前用户
    1. 封装用户登录信息生成Token
    1. 执行登录操作,可自定义捕获异常
  1. controller中配置login
@RequestMapping("/login")
    public String login(String username,String pwd,Model model){
        // 1. 获取当前用户
        Subject subject = SecurityUtils.getSubject();
        // 2. 封装用户信息
        UsernamePasswordToken token = new UsernamePasswordToken(username, pwd);
        try{
            // 3. 执行登录操作 成功后返回首页
            subject.login(token);
            return "index";
        }catch (UnknownAccountException e){
            // 捕获异常
            model.addAttribute("msg","登陆失败!用户名不存在!!");
            return "login";
        }catch (IncorrectCredentialsException e){
            // 捕获异常
            model.addAttribute("msg","登陆失败!密码错误!!");
            return "login";
        }
    }
  1. login.html中添加msg显示
<p th:text="${msg}" style="color: red"></p>
  1. UserRealm中编写认证检查逻辑
/*用户认证*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("调用了=========用户认证方法");
        // 自定义用户 也可从数据库中查询
        String username = "root";
        String pwd = "123";
        // 获取Token————controller中使用后,全局可用
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        // 判断用户名
        if(!username.equals(token.getUsername())){
            // 若不一致,自动抛出异常
            return null;
        }
        // Shiro帮我们判断密码
        return new SimpleAuthenticationInfo("",pwd,"");
    }

总结:获取表单中的用户信息,与设定好的信息进行比对,一致即可认证成功,成功登陆。、

仅用比对用户名,密码Shiro会帮我们自动比对。

案例四:整合MyBatis,连接数据库

  1. 导入mysql连接、Druid、MyBatis-Spring等jar包
<!--数据库连接-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>
<!--Log4J-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<!--Druid-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.6</version>
</dependency>
<!--MyBatis-Spring-starter-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>
<!--lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.16</version>
</dependency>
  1. 编写application.yaml和application.properties配置文件

application.properties

mybatis.type-aliases-package=com.zy.pojo
mybatis.mapper-locations=classpath:mapper/*.xml

application.yaml

spring:
  datasource:
    username: root
    password: zy123456
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
  1. 编写实体类pojo
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}
  1. 编写Dao层UserMapper,UserMapper.xml
@Repository
@Mapper
public interface UserMapper {
    User getUserByName(@Param("name") String name);
}
  1. 编写service层 UserService,UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;

    @Override
    public User getUserByName(String name) {
        return userMapper.getUserByName(name);
    }
}
  1. UserRealm中查询数据库获取用户
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
     System.out.println("调用了=========用户认证方法");
     // 1. 获取Token————controller中使用后,全局可用
     UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

     // 2. 数据库中查询是否存在
     User user = userService.getUserByName(token.getUsername());
     if (user!=null){
            // 3. Shiro帮我们判断密码
            return new SimpleAuthenticationInfo(user,user.getPwd(),"");
     }
     // 用户不存在,自动抛出异常
     return null;
}

总结:从数据库中查询用户数据,与登录的数据进行比对,一致即可认证成功。

案例五:实现用户权限功能

数据库中user表添加perms字段,用于设置权限

  1. ShrioConfiguration中 更改权限设置
 		HashMap<String, String> map = new HashMap<>();
        map.put("/user/update","perms[user:update]");// perms值为user:update 可进入更新页面
        map.put("/user/add","perms[user:add]");//  perms值为user:add 可进入添加页面
        map.put("/user/show","authc"); // 登录查看
        // 设置拦截权限
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        // 拦截后跳转到登录页面
        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        // 没有权限后显示  用户被拦截
        shiroFilterFactoryBean.setUnauthorizedUrl("/error401");
        return shiroFilterFactoryBean;
  1. 设置未授权响应字符串即controller访问路径
	@RequestMapping("/error401")
	@ResponseBody
	public String error401(){
	    return "未经授权,无法访问该页面!";
	}
  1. UserRealm中设置用户授权操作
/*用户授权*/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("调用了=========用户授权方法");
        // info:用户授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 获取当前用户
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();
        // 获取用户perms属性的值,用于设置权限
        info.addStringPermission(user.getPerms());
        return info;
    }

结果:数据库中添加perms字段,用于设置用户权限,拥有权限的用户才可访问,否则进入/error401,显示没有权限的信息。

案例六:实现用户注销功能

  1. controller中设置路径
 @RequestMapping("/logout")
    public String logout(HttpSession session){
        // 1. 获取当前用户
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/toLogin";
    }
  1. ShrioConfiguration的map中添加权限
		map.put("/logout","logout");
        // 设置拦截权限
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
  1. index.html中添加注销链接
<a th:href="@{/logout}">注销</a>

总结:用户可注销。

案例七:实现部分显示功能

  1. Shiro整合thymeleaf,导入对应jar包
<!-- Shiro整合thymeleaf-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
  1. 在ShrioConfiguration中装配thymeleaf
    // 装配shiro整合thymeleaf
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
  1. 修改index.html

注意:导入Shiro整合thymeleaf命名空间

<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">

要求:对用权限的用户显示对应信息,未登录显示登录,已登录显示注销

<!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 th:align="center">
<h1>首页</h1>
    <!--未登录显示登录-->
    <shiro:notAuthenticated>
        <a th:href="@{/toLogin}">登录</a>
    </shiro:notAuthenticated>
    <!--登陆之后显示注销-->
    <shiro:authenticated>
        <span th:text="${username}">用户名:</span>
        <a th:href="@{/logout}">注销</a>
    </shiro:authenticated>
<hr>
<a th:href="@{/user/show}">展示页面</a>
<div shiro:hasPermission="user:add">
     <a th:href="@{/user/add}">添加页面</a>
</div>
<div shiro:hasPermission="user:update">
    <a th:href="@{/user/update}">修改页面</a>
</div>
</body>
</html>

六、整体项目

项目结构
在这里插入图片描述

  1. Pom.xml依赖
<dependencies>
        <!-- Shiro整合thymeleaf-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
        <!--数据库连接-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--Log4J-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!--Druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.6</version>
        </dependency>
        <!--MyBatis-Spring-starter-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>
        <!--Shiro-Spring-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.6.0</version>
        </dependency>
        <!--thymeleaf-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
</dependencies>
  1. ShrioConfiguration配置文件
@Configuration
public class ShrioConfiguration {
    // 装配ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager ){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        // 添加Shiro内置过滤器
         /*
            anon:无需认证就可以访问
            authc:必须认证了才能访问
            user:必须拥有记住我功能才能使用
            perms:拥有对某个资源的权限才能访问
            role:拥有某个角色权限才能访问
         */
        HashMap<String, String> map = new HashMap<>();
        map.put("/user/update","perms[user:update]");// perms值为user:update 可进入更新页面
        map.put("/user/add","perms[user:add]");//  perms值为user:add 可进入添加页面
        map.put("/user/show","anon"); // 直接查看
        map.put("/logout","logout");
        // 设置拦截权限
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        // 拦截后跳转到登录页面
        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        // 没有权限后显示  用户被拦截
        shiroFilterFactoryBean.setUnauthorizedUrl("/error401");

        return shiroFilterFactoryBean;
    }
    // 装配SecurityManager,注入自定义的UserRealm
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        // 关联自定义的UserRealm
        defaultWebSecurityManager.setRealm(userRealm);
        return defaultWebSecurityManager;
    }
    // 装配Realm
    @Bean(name = "userRealm")
    public UserRealm getUserRealm(){
        return new UserRealm();
    }
    // 装配shiro整合thymeleaf
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}
  1. 自定义UserRealm
public class UserRealm extends AuthorizingRealm {
    @Autowired
    UserServiceImpl userService;

    /*用户认证*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("调用了=========用户认证方法");
        // 1. 获取Token————controller中使用后,全局可用
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

        // 2. 数据库中查询是否存在
        User user = userService.getUserByName(token.getUsername());
        if (user!=null){
            // 3. Shiro帮我们判断密码
            return new SimpleAuthenticationInfo(user,user.getPwd(),"");
        }
        // 用户不存在,自动抛出异常
        return null;
    }

    /*用户授权*/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("调用了=========用户授权方法");
        // info:用户授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 获取当前用户
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();
        // 获取用户perms属性的值,用于设置权限
        info.addStringPermission(user.getPerms());
        return info;
    }
}
  1. testController控制跳转
@Controller
public class testController {
    @RequestMapping("/index")
    public String toIndex(Model model){
        return "index";
    }
    @RequestMapping("/user/add")
    public String add(){
        return "/user/add";
    }
    @RequestMapping("/user/update")
    public String update(){
        return "/user/update";
    }
    @RequestMapping("/user/show")
    public String show(){
        return "/user/show";
    }
    @RequestMapping("/toLogin")
    public String toLogin(){
        return "login";
    }
    @RequestMapping("/logout")
    public String logout(HttpSession session){
        // 1. 获取当前用户
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/toLogin";
    }
    @RequestMapping("/login")
    public String login(String username,String pwd,Model model){
        // 1. 获取当前用户
        Subject subject = SecurityUtils.getSubject();
        // 2. 封装用户信息
        UsernamePasswordToken token = new UsernamePasswordToken(username, pwd);
        model.addAttribute("username",token.getUsername());
        try{
            // 3. 执行登录操作 成功后返回首页
            subject.login(token);
            return "index";
        }catch (UnknownAccountException e){
            // 捕获异常
            model.addAttribute("msg","登陆失败!用户名不存在!!");
            return "login";
        }catch (IncorrectCredentialsException e){
            // 捕获异常
            model.addAttribute("msg","登陆失败!密码错误!!");
            return "login";
        }
    }
    @RequestMapping("/error401")
    @ResponseBody
    public String error401(){
        return "未经授权,无法访问该页面!";
    }
}
  1. 数据库

User实体类

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

UserMapperDoa层

@Repository
@Mapper
public interface UserMapper {
    User getUserByName(@Param("name") String name);
}

UserMapper.xml具体sql

<?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.zy.dao.UserMapper">

    <select id="getUserByName" resultType="User">
        select * from mybatis.user
        where name=#{name}
    </select>
</mapper>

UserService实现

public interface UserService {
    // 根据姓名查询
    User getUserByName(String name);
}

UserServiceImpl实现类

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;

    @Override
    public User getUserByName(String name) {
        return userMapper.getUserByName(name);
    }
}
  1. 配置文件

application.properties

mybatis.type-aliases-package=com.zy.pojo
mybatis.mapper-locations=classpath:mapper/*.xml

application.yaml

spring:
  datasource:
    username: root
    password: zy123456
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
  1. 前端网页

index.html

<body th:align="center">
<h1>首页</h1>
    <!--未登录显示登录-->
    <shiro:notAuthenticated>
        <a th:href="@{/toLogin}">登录</a>
    </shiro:notAuthenticated>
    <!--登陆之后显示注销-->
    <shiro:authenticated>
        用户名:<span th:text="${username}"></span> <br> <br>
        <a th:href="@{/logout}">注销</a>
    </shiro:authenticated>
<hr>
<a th:href="@{/user/show}">展示页面</a> <br>
<div shiro:hasPermission="user:add">
     <a th:href="@{/user/add}">添加页面</a>
</div> <br>
<div shiro:hasPermission="user:update">
    <a th:href="@{/user/update}">修改页面</a>
</div>  <br>
</body>

login.html

<body th:align="center">
<h1>登录</h1>
<hr>
<p th:text="${msg}" style="color: red"></p>
<form method="post" action="/login">
    用户名:<input type="text" name="username">
    密码:<input type="password" name="pwd">
    <input type="submit" value="提交">
</form>
</body>

add.html

<body align="center">
<h2>add页面</h2>
</body>

show.html

<body align="center">
<h2>show页面</h2>
</body>

update.html

<body align="center">
<h2>update页面</h2>
</body>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值