shiro整合SpringBoot项目实战(jsp版本)

一、整合思路

在这里插入图片描述

二、创建springboot项目

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

  • pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--引入jsp解析依赖-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
        <!--引入jstl(使用c标签)-->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!--shiro整合springboot的依赖-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.5.3</version>
        </dependency>

        <!--mybatis相关依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.19</version>
        </dependency>
    </dependencies>
  • application.properties
server.port=8088
server.servlet.context-path=/shiro
spring.application.name=shiro


spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp



spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/shiro?characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456


mybatis.type-aliases-package=com.ahnu.springboot_jsp_shiro.entity
mybatis.mapper-locations=classpath:com/ahnu/mapper/*.xml

三、 配置shiro环境

1、创建ShiroConfig类

/**
 * shiro的配置类
 */
@Configuration
public class ShiroConfig {
    // 1.创建shiro的拦截器 用来拦截所有的请求
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 给filter设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        // 配置系统的受限资源
        // 配置系统的公共资源
        Map<String, String> map = new HashMap<>();
        map.put("/user/login", "anon");      // anon代表可以匿名访问的资源
        map.put("/user/register", "anon");      // anon代表可以匿名访问的资源
        map.put("/register.jsp", "anon");      // anon代表可以匿名访问的资源
        map.put("/**", "authc");  // authc  表示请求这个路径需要认证和授权

        // 默认认证界面路径
        shiroFilterFactoryBean.setLoginUrl("/login.jsp");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    // 2.创建安全管理器
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        // 给安全管理器注入realm
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }
    // 3.创建自定义的realm
    @Bean
    public Realm getRealm(){
        CustomRealm customRealm = new CustomRealm();
        // 设置hash散列凭证气你
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");
        hashedCredentialsMatcher.setHashIterations(1024);
        customRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return customRealm;
    }
}

2、创建自定义realm

public class CustomRealm extends AuthorizingRealm {

    // 授权处理
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    // 认证处理
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("========进入认证处理=========");
        // 获取用户信息
        String principal = (String) token.getPrincipal();

        UserService userService = (UserService) ApplicationContextUtil.getBean("userService");
        // 根据用户名查询数据库
        User user = userService.findByUserName(principal);
        if (!ObjectUtils.isEmpty(user)){
            return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),
                    ByteSource.Util.bytes(user.getSalt()),this.getName());
        }

        return null;
    }
}

3、工具类的创建

  • ApplicationContextUtil
// 获取容器的工具类
@Component
public class ApplicationContextUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    // 根据bean名字获取工厂中指定bean 对象
    public static Object getBean(String beanName){
        return applicationContext.getBean(beanName);
    }
}
  • SaltUtil
// 生成salt
public class SaltUtil {

    public static String getSalt(int n){
        char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890@!#$%*".toCharArray();
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < n; i++) {
            char aChar = chars[new Random().nextInt(chars.length)];
            stringBuilder.append(aChar);
        }
        return stringBuilder.toString();
    }

    // 测试
    public static void main(String[] args) {
        System.out.println(getSalt(8));
    }
}

四、认证的实现

1、entity实体

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String id;
    private String username;
    private String password;
    private String salt;
}
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `username` varchar(40) DEFAULT NULL,
  `password` varchar(40) DEFAULT NULL,
  `salt` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

SET FOREIGN_KEY_CHECKS = 1;

2、dao层

public interface UserDAO {
    // 保存用户信息
    void save(User user);
    // 根据用户名查询信息
    User findByUserName(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.ahnu.springboot_jsp_shiro.dao.UserDAO">
    <insert id="save" parameterType="User" keyProperty="id" useGeneratedKeys="true">
        insert into t_user values (#{id},#{username},#{password},#{salt})
    </insert>

    <select id="findByUserName" resultType="User" parameterType="String">
        select id,username,password,salt from t_user where username=#{username}
    </select>
</mapper>

3、service层

public interface UserService {
    void register(User user);
    // 根据用户名查询信息
    User findByUserName(String username);
}
@Service("userService")
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    UserDAO userDAO;

    @Override
    public void register(User user) {
        // salt
        String salt = SaltUtil.getSalt(4);
        // 明文 + salt + hash散列
        Md5Hash md5Hash = new Md5Hash(user.getPassword(), salt, 1024);
        // 保存加密的密码和salt
        user.setPassword(md5Hash.toHex()).setSalt(salt);
        // 调用dao层的保存方法
        userDAO.save(user);
    }

    @Override
    public User findByUserName(String username) {
        return userDAO.findByUserName(username);
    }
}

4、controller层

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    UserService userService;

    // 用户注册
    @PostMapping("/register")
    public String register(User user){
        try {
            userService.register(user);
            return "redirect:/login.jsp";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "redirect:/register.jsp";
    }

    // 退出用户登录
    @GetMapping("/logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/login.jsp";
    }



    // 用户登录
    @PostMapping("/login")
    public String login(String username, String password){
        // 获取用户主体
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            // 实现登录
            subject.login(token);
            return "redirect:/index.jsp";
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户名错误!");
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            System.out.println("密码错误!");
        }
        return "redirect:/login.jsp";
    }
}

5、webapp资源

  • index.jsp
<%@page contentType="text/html;UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <%--受限资源--%>
    <h1>系统主页V1.0</h1>
    <a href="${pageContext.request.contextPath}/user/logout">退出登录</a>
    <ul>
        <li><a href="">用户管理</a></li>
        <li><a href="">商品管理</a></li>
        <li><a href="">订单管理</a></li>
        <li><a href="">物流管理</a></li>
    </ul>
</body>
</html>
  • register.jsp
<%@page contentType="text/html;UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <%--公共资源--%>
    <h1>欢迎注册!</h1>
    <form action="${pageContext.request.contextPath}/user/register" method="post">
        用户名:<input type="text" name="username"> <br>
        密码: <input type="text" name="password"> <br>
        <input type="submit" value="注册">
    </form>
</body>
</html>
  • login.jsp
<%@page contentType="text/html;UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <%--公共资源--%>
    <h1>欢迎登录!</h1>
    <form action="${pageContext.request.contextPath}/user/login" method="post">
        用户名:<input type="text" name="username"> <br>
        密码: <input type="text" name="password"> <br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

6、测试

在这里插入图片描述
在这里插入图片描述

五、授权的实现

1、数据库表的设计

在这里插入图片描述

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_pers
-- ----------------------------
DROP TABLE IF EXISTS `t_perms`;
CREATE TABLE `t_pers` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `name` varchar(80) DEFAULT NULL,
  `url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_role
-- ----------------------------
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `name` varchar(60) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_role_perms
-- ----------------------------
DROP TABLE IF EXISTS `t_role_perms`;
CREATE TABLE `t_role_perms` (
  `id` int(6) NOT NULL,
  `roleid` int(6) DEFAULT NULL,
  `permsid` int(6) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `username` varchar(40) DEFAULT NULL,
  `password` varchar(40) DEFAULT NULL,
  `salt` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_user_role
-- ----------------------------
DROP TABLE IF EXISTS `t_user_role`;
CREATE TABLE `t_user_role` (
  `id` int(6) NOT NULL,
  `userid` int(6) DEFAULT NULL,
  `roleid` int(6) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

SET FOREIGN_KEY_CHECKS = 1;

2、实体类的设计

在这里插入图片描述

3、在dao层中添加方法

// 根据用户名查询用户所属的角色信息
    User findRolesByUserName(String username);
    // 通过角色id查询权限信息
    List<Permission> findPerByRoleId(String id);
	 <resultMap id="userMap" type="User">
        <id column="uid" property="id" />
        <result column="username" property="username" />
        <!--角色信息-->
        <collection property="roles" javaType="list" ofType="Role">
            <id column="id" property="id" />
            <result column="rname" property="name" />
        </collection>
    </resultMap>
	<!--根据用户名查询用户的角色信息-->
    <select id="findRolesByUserName" parameterType="String" resultMap="userMap">
        SELECT
            u.id uid,
            u.username,
            r.id,
            r. NAME rname
        FROM
            t_user u
                LEFT JOIN t_user_role ur ON u.id = ur.userid
                LEFT JOIN t_role r ON ur.roleid = r.id
        WHERE
            u.username = #{username}
    </select>

    <select id="findPerByRoleId" parameterType="String" resultType="Permission">
        SELECT
            p.id,
            p. NAME,
            p.url,
            r. NAME
        FROM
            t_role r
                LEFT JOIN t_role_perms rp ON r.id = rp.roleid
                LEFT JOIN t_perms p ON rp.permsid = p.id
        WHERE
            r.id = #{id}
    </select>

4、在service中添加方法

// 根据用户名查询用户所属的角色信息
    User findRolesByUserName(String username);
    // 通过角色id查询权限信息
    List<Permission> findPerByRoleId(String id);
@Override
    public User findRolesByUserName(String username) {
        return userDAO.findRolesByUserName(username);
    }

    @Override
    public List<Permission> findPerByRoleId(String id) {
        return userDAO.findPerByRoleId(id);
    }

5、修改CustomRealm类

public class CustomRealm extends AuthorizingRealm {

    // 授权处理
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("========进入授权处理=========");
        // 获取用户身份信息
        String principal = (String) principalCollection.getPrimaryPrincipal();
        // 根据用户信息去查询数据库
        UserService userService = (UserService) ApplicationContextUtil.getBean("userService");
        // 根据用户名查询数据库
        User user = userService.findRolesByUserName(principal);
        if (!ObjectUtils.isEmpty(user)){
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            user.getRoles().forEach(role -> {
                // 添加角色信息
                simpleAuthorizationInfo.addRole(role.getName());
                System.out.println(role.getId()+"=============>");
                // 添加权限信息
                List<Permission> perms = userService.findPerByRoleId(role.getId());
                if (!CollectionUtils.isEmpty(perms)){
                    perms.forEach(perm -> {
                        simpleAuthorizationInfo.addStringPermission(perm.getName());
                    });
                }
            });
            return simpleAuthorizationInfo;
        }
        return null;
    }

    // 认证处理
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("========进入认证处理=========");
        // 获取用户信息
        String principal = (String) token.getPrincipal();

        UserService userService = (UserService) ApplicationContextUtil.getBean("userService");
        // 根据用户名查询数据库
        User user = userService.findByUserName(principal);
        if (!ObjectUtils.isEmpty(user)){
            return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),
                    ByteSource.Util.bytes(user.getSalt()),this.getName());
        }

        return null;
    }
}

6、测试

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值