Shiro整合Springboot+Mybatis

Shiro是一款开源安全框架,Shiro提供的Api可以用于 :身份验证(登录)、授权-访问控制、会话管理、加密。
Shiro官网:
目前Shiro和SpringSecurity一样经常被用与和Spring的各种框架整合使用
学习Shiro主要
在这里插入图片描述
Subject:当前用户
Shiro SecurityManager:安全管理器,管理着所有 Subject
Realm:域,放着用户认证和授权的安全信息,通常由我们自己自定义一个域,然后注入给SecurityManager,SecurityManager再判断该用户的权限。

做个小Demo进行理解一下
SpringBoot+Mybatis+thymeleaf+mysql

  1. 首页先创建一个Spirngboot的项目,导入依赖。
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</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>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
    </dependencies>
  1. 创建数据库:
/*
SQLyog Ultimate v12.08 (64 bit)
MySQL - 5.7.33 : Database - db_shiro
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`db_shiro` /*!40100 DEFAULT CHARACTER SET utf8 */;

USE `db_shiro`;

/*Table structure for table `permission` */

DROP TABLE IF EXISTS `permission`;

CREATE TABLE `permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `permissionname` varchar(50) NOT NULL COMMENT '权限名',
  `role_id` int(11) DEFAULT NULL COMMENT '外键关联role',
  PRIMARY KEY (`id`),
  KEY `role_id` (`role_id`),
  CONSTRAINT `permission_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

/*Data for the table `permission` */

insert  into `permission`(`id`,`permissionname`,`role_id`) values (1,'user:*',1),(2,'del:*',2);

/*Table structure for table `role` */

DROP TABLE IF EXISTS `role`;

CREATE TABLE `role` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `rolename` varchar(20) DEFAULT NULL COMMENT '角色名称',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

/*Data for the table `role` */

insert  into `role`(`id`,`rolename`) values (1,'admin'),(2,'user'),(3,'user1');

/*Table structure for table `user` */

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户主键',
  `username` varchar(20) NOT NULL COMMENT '用户名',
  `password` varchar(20) NOT NULL COMMENT '密码',
  `role_id` int(11) DEFAULT NULL COMMENT '外键关联role表',
  PRIMARY KEY (`id`),
  KEY `role_id` (`role_id`),
  CONSTRAINT `user_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

/*Data for the table `user` */

insert  into `user`(`id`,`username`,`password`,`role_id`) values (1,'ctr1','123456',1),(2,'ctr2','123456',2),(3,'ctr3','123456',3);

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

  1. 创建这3个表的实体类Permission、Role、User
@Data
public class Permission {
    private Integer id;
    private String permissionName;
    private Integer roleId;
}
@Data
public class Role {
    private Integer id;
    private String roleName;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String userName;
    private String passWord;
    private Integer roleId;
    public User(String userName, String passWord){
        this.userName = userName;
        this.passWord = passWord;
    }
}
  1. 创建Mapper和Mapper.xml、Service后续认证和授权需要查数据库
@Repository
@Mapper
public interface UserMapper {
    Set<String> getRoles(String userName);
    Set<String> getPermissions(String userName);
    // @Select("select * from db_shiro.t_user where username = #{userName}")
    User getByUserName(String userName);
}
<?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.ctr.mapper.UserMapper">
    <select id="getRoles" resultType="String" parameterType="String">
    select r.rolename from db_shiro.user u, db_shiro.role r where u.username= #{userName} and u.role_id = r.id;
    </select>

    <select id="getPermissions" resultType="String" parameterType="String">
        select p.permissionname from db_shiro.user u, db_shiro.permission p where u.username=#{userName} and u.role_id = p.role_id;
    </select>

    <select id="getByUserName" resultType="User" parameterType="String">
        select * from db_shiro.user where username = #{userName};
    </select>
</mapper>
public interface UserService {
    Set<String> getRoles(String userName);
    Set<String> getPermissions(String userName);
    User getByUserName(String userName);
}
package com.ctr.service;

import com.ctr.mapper.UserMapper;
import com.ctr.pojo.Permission;
import com.ctr.pojo.Role;
import com.ctr.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Set;

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

    @Override
    public Set<String> getRoles(String userName) {
        return userMapper.getRoles(userName);
    }

    @Override
    public Set<String> getPermissions(String userName) {
        return userMapper.getPermissions(userName);
    }

    @Override
    public User getByUserName(String userName) {
        return userMapper.getByUserName(userName);
    }

}

  1. 自定义Realm,继承AuthorizingRrealm,重写doGetAuthorizationInfo(授权)、doGetAuthenticationInfo(认证)方法
public class MyRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 获取用户名
        String username = (String) principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        // 给该用户设置角色信息
        authorizationInfo.setRoles(userService.getRoles(username));
        // 给该用户设置权限信息
        authorizationInfo.setStringPermissions(userService.getPermissions(username));

        return authorizationInfo;

    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 根据 authenticationToken 获取用户名
        String username = (String) authenticationToken.getPrincipal();

        // 根据用户名从数据库中查找该用户

        User user = userService.getByUserName(username);

        if (user != null){
            // 把当前用户存到Session中
            SecurityUtils.getSubject().getSession().setAttribute("user",user);
            AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUserName(),user.getPassWord(), "myRealm");
            return authcInfo;
        }else {
            return null;
        }
    }
}
  1. @Configuration 配置Shiro,注入自定义Realm、SecurityManager 、shiroFilter,项目启动时,就注入到环境中
@Configuration
public class ShiroConfig {
    private static final Logger logger = LoggerFactory.getLogger(ShiroConfig.class);

    @Bean
    public MyRealm myAuthRealm(){
        MyRealm myRealm = new MyRealm();
        logger.info("========myRealm注册完成=======");
        return myRealm;
    }

    @Bean
    public SecurityManager securityManager(){
        DefaultSecurityManager securityManager = new DefaultWebSecurityManager(myAuthRealm());
        logger.info("====securityManager注册完成====");
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
        // 定义 shiroFactoryBean
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 设置自定义的 securityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 设置默认登录地址,登录认证失败会访问该地址
        shiroFilterFactoryBean.setLoginUrl("/user/login");
        // 设置成功之后要跳转的地址
        shiroFilterFactoryBean.setSuccessUrl("/success");

        // 设置未授权界面
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
        // 拦截器配置
        Map<String,String> filterChainMap = new LinkedHashMap<>();
        // 配置放行资源,anon 表示放行
        filterChainMap.put("/css/**", "anon");
        filterChainMap.put("/imgs/**", "anon");
        filterChainMap.put("/js/**", "anon");
        filterChainMap.put("/swagger-*/**", "anon");
        filterChainMap.put("/swagger-ui.html/**", "anon");
        filterChainMap.put("/user/login","anon");

        // 以"/user/admin/开头的用户需要身份验证, authc表示要进行身份认证
        filterChainMap.put("/user/admin*","authc");
       ///user/dog” 开头的用户需要角色认证,是“admin”才允许
        filterChainMap.put("/user/dog*/**", "roles[admin]");
        // “/user/cat” 开头的用户需要权限认证,是“user:create”才允许
        
        filterChainMap.put("/user/cat*/**", "perms[\"user:create\"]");
        // 配置logout过滤器
        filterChainMap.put("/logout","logout");
        
        // 设置shiroFilterFactoryBean 的setFilterChainDefinitionMap
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
        logger.info("========shiroFilterFactoryBean注册完成=======");
        return shiroFilterFactoryBean;
    }

}
  1. Controller编写接口,当用户请求登录接口(user/login)时,根据前端传来的账号密码创建一个token,通过SecurityUtils.getSubject()创建当前用户主体,subject.login(token)来到自定义的Realm中的doGetAuthenticationInfo开始认证
@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/admin")
    public String admin(HttpServletRequest request) {
        Object user = request.getSession().getAttribute("user");
        return "success";
    }


    @RequestMapping("/dog")
    public String dog(HttpServletRequest request) {
        return "success";
    }


    @RequestMapping("/cat")
    public String cat(HttpServletRequest request) {
        return "success";
    }

    @RequestMapping("/login")
    public String login(User user, HttpServletRequest request) {
        // 根据用户名和密码创建 Token
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassWord());
        // 获取 subject 认证主体
        Subject subject = SecurityUtils.getSubject();
        try{
            // 开始认证,这一步会跳到我们自定义的 Realm 中
            subject.login(token);
            request.getSession().setAttribute("user", user);
            return "success";
        }catch(Exception e){
            e.printStackTrace();
            request.getSession().setAttribute("user", user);
            request.setAttribute("error", "用户名或密码错误!");
            return "login";
        }
    }

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

application.yml

server:
  port: 8086
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/db_shiro?charset=true&characterEncoding=utf8
    password: 123456
    username: root
    driver-class-name: com.mysql.cj.jdbc.Driver
  web:
    resources:
      static-locations: classpath:/templates/
mybatis:
  mapper-locations: classpath:mybatis/mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true
  type-aliases-package: com.ctr.pojo
  1. 编写html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/XSL/Transform">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<p th:text="${error}"></p>
<form th:action="@{/user/login}" method="post">
    <p>账号:<input type="text" name="userName"></p>
    <p>密码:<input type="password" name="passWord"></p>
    <p><input type="submit" value="登录"></p>
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>登录成功</title>
</head>
<body>
<h1>恭喜你登录成功</h1>
<a th:href="@{/user/logout}">注销</a>
</body>
</html>
  1. 测试
    当请求http://localhost:8086/user/admin[cat/dog]时,因为在ShiroConfig中配置了未登录重定向到/user/login,此处有个bug因为我直接走了这个请求,所以请求携带error。
    在这里插入图片描述
    输入ctr1,123456登录成功
    在这里插入图片描述
    成功后均可访问http://localhost:8086/user/dog、http://localhost:8086/user/cat
    注销后回到登录页面,用ctr2,123456登录成功后访问http://localhost:8086/user/dog、http://localhost:8086/user/cat,重定向到未授权页面
    在这里插入图片描述

一个简单的登录授权的小Demo完成了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这个框架整合可以分为以下几个步骤: 1. 集成 Spring Boot:在 pom.xml 文件中添加 Spring Boot 依赖,创建 Spring Boot 启动类。 2. 集成 MyBatis Plus:在 pom.xml 文件中添加 MyBatis Plus 依赖,配置数据源和 MyBatis Plus 相关配置。 3. 集成 Shiro:在 pom.xml 文件中添加 Shiro 依赖,创建 Shiro 配置类,配置 Shiro 的 Realm 和 SecurityManager。 4. 集成 JWT:在 pom.xml 文件中添加 jjwt 依赖,创建 JWT 工具类,用于生成和解析 JWT。 下面是一个简单的示例代码,你可以根据你的实际需求进行调整: 1. pom.xml 文件中添加依赖: ```xml <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MyBatis Plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</version> </dependency> <!-- Shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency> <!-- jjwt --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>${jjwt.version}</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>${jjwt.version}</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>${jjwt.version}</version> <scope>runtime</scope> </dependency> ``` 2. 创建 Spring Boot 启动类: ```java @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 3. 配置 MyBatis Plus: ```java @Configuration @MapperScan("com.example.mapper") public class MyBatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } } ``` 4. 配置 Shiro: ```java @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); shiroFilter.setSecurityManager(securityManager); shiroFilter.setUnauthorizedUrl("/401"); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/401", "anon"); filterChainDefinitionMap.put("/**", "jwt"); shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap); Map<String, Filter> filters = new LinkedHashMap<>(); filters.put("jwt", new JwtFilter()); shiroFilter.setFilters(filters); return shiroFilter; } @Bean public DefaultWebSecurityManager securityManager(Realm realm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm); return securityManager; } @Bean public Realm realm() { return new UserRealm(); } } ``` 5. 创建 JWT 工具类: ```java public class JwtUtil { private static final String SECRET_KEY = "your_secret_key"; private static final long EXPIRATION_TIME = 3600_000; // 1 hour public static String generateToken(String username) { Date now = new Date(); Date expiration = new Date(now.getTime() + EXPIRATION_TIME); return Jwts.builder() .setSubject(username) .setIssuedAt(now) .setExpiration(expiration) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact(); } public static String getUsernameFromToken(String token) { Claims claims = Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); return claims.getSubject(); } public static boolean validateToken(String token) { try { Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token); return true; } catch (JwtException e) { return false; } } } ``` 6. 创建 JwtFilter: ```java public class JwtFilter extends AuthenticatingFilter { @Override protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) { HttpServletRequest request = (HttpServletRequest) servletRequest; String token = request.getHeader("Authorization"); if (StringUtils.isBlank(token)) { return null; } return new JwtToken(token); } @Override protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { HttpServletResponse response = (HttpServletResponse) servletResponse; response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.getWriter().write("{\"code\":401,\"message\":\"未登录或登录已过期,请重新登录\"}"); return false; } @Override protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception { HttpServletRequest request = (HttpServletRequest) servletRequest; String token = request.getHeader("Authorization"); if (StringUtils.isBlank(token)) { return false; } return JwtUtil.validateToken(token); } } ``` 7. 创建 JwtToken: ```java public class JwtToken implements AuthenticationToken { private final String token; public JwtToken(String token) { this.token = token; } @Override public Object getPrincipal() { return JwtUtil.getUsernameFromToken(token); } @Override public Object getCredentials() { return token; } } ``` 8. 创建 UserRealm: ```java public class UserRealm extends AuthorizingRealm { @Autowired private UserService userService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { String username = (String) authenticationToken.getPrincipal(); User user = userService.getByUsername(username); if (user == null) { throw new UnknownAccountException("账号不存在"); } return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName()); } } ``` 这样,简单的 Spring Boot + Shiro + JWT + MyBatis Plus 整合就完成了。你可以根据具体的需求,对代码进行修改和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值