第22章 一文读懂:Spring Boot集成Shiro实现安全防护

在上一篇文章【Springboot整合Spring Security实现安全控制和权限方案】中,我们使用Spring Boot应用中集成了Spring Security,并实现了基本的认证与授权功能,同时也掌握了如何使用Thymeleaf与Spring Security结合来控制页面的访问权限。接下来,我们将探索另一种安全解决方案——集成Shiro,它将为我们提供另一套强大的安全防护机制,让我们在不同的场景下能够更加灵活地选择合适的安全框架来保护应用。

任务描述

任务要求

使用IDEA开发工具构建一个项目多模块工程。study-springboot-chapter15学习关于Springboot集成shiro实现安全认证和授权知识点

  1. 基于study-springboot工程,复制study-springboot-chapter15标准项目,坐标groupId(com.cbitedu)、artifactId(study-springboot-chapter15),其他默认
  2. 继承study-springboot工程依赖
  3. 引入Apache Shiro安全框架,实现系统认证和授权
  4. 持久化层使用JPA实现

任务收获

  1. Spring Boot中整合Apache Shiro框架
  2. 学会使用JUnit完成单元测试
  3. Apache shiro权限实现原理
  4. 掌握web开发原理
  5. 熟悉SpringMVC的基础配置
  6. JPA持久化技术的应用

任务准备

环境要求

  1. JDK1.8+
  2. MySQL8.0.27+
  3. Maven 3.6.1+
  4. IDEA/VSCode

数据库准备




DROP TABLE IF EXISTS t_sys_userinfo;
CREATE TABLE t_sys_userinfo  (
  user_id varchar(32)  NOT NULL,
  username varchar(50)  NOT NULL COMMENT '用户名',
  password varchar(100)   COMMENT '密码',
  salt varchar(20)   COMMENT '盐',
  email varchar(100)   COMMENT '邮箱',
  mobile varchar(100)   COMMENT '手机号',
  status tinyint  COMMENT '状态  0:禁用   1:正常',
  create_user_id varchar(32)   COMMENT '创建者ID',
  create_time varchar(32)   COMMENT '创建时间',
  userimg varchar(255)   COMMENT '用户头像',
  zip varchar(10)   COMMENT '邮政编码',
  sort_num int  COMMENT '排序号',
  user_type varchar(10)   COMMENT '用户类型',
  post_id varchar(32)   COMMENT '所属岗位',
  sex varchar(4)   COMMENT '性别',
  USER_REALNAME varchar(50)   COMMENT '真实姓名',
  user_theme varchar(255)   COMMENT '用户选择皮肤',
  user_card varchar(18)   COMMENT '身份证号码',
  birthday varchar(20)   COMMENT '出生年月',
  native_place varchar(255)   COMMENT '家庭住址',
  nation varchar(255)   COMMENT '民族',
  update_user_id varchar(32)   COMMENT '创建者ID',
  update_time varchar(32)   COMMENT '创建时间',
  PRIMARY KEY (user_id) USING BTREE,
  UNIQUE INDEX username(username ASC) USING BTREE
) COMMENT = '系统用户' ;




INSERT INTO t_sys_userinfo VALUES ('1', 'admin', 'efc7b4b496e9db2e7a605db183f9d831', 'YzcmCZNvbXocrsz9dm8e', 'cc@bluefairy.com', '18929423839', 1, '1', '2016-11-11 11:11:11', NULL, NULL, NULL, NULL, NULL, NULL, '系统管理员', 'green', NULL, NULL, NULL, NULL, NULL, NULL);

工程目录要求

study-springboot-chapter15

源码地址

study-springboot-chapter15

任务实施

在所有的开发的系统中,都必须做认证(authentication)和授权(authorization),以保证系统的安全性。

  1. authentication: 【认证】你要登录论坛,输入用户名张三,密码 1234,密码正确,证明你张三确实是张三,这就是 authentication。
  2. authorization:【授权】再一 check 用户张三是个版主,所以有权限加精删别人帖,这就是 authorization 。

所以简单来说:认证解决“你是谁”的问题,授权解决“你能做什么”的问题。

第一步:基于标准Springboot工程模板study-springboot-chapter00继承父工程,在pom.xml中加入所需的依赖:

   
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-jpaartifactId>
        dependency>
        
        <dependency>
            <groupId>org.apache.shirogroupId>
            <artifactId>shiro-webartifactId>
            <version>1.9.1version>
        dependency>
        <dependency>
            <groupId>org.apache.shirogroupId>
            <artifactId>shiro-springartifactId>
            <version>1.9.1version>
        dependency>

        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            <version>1.2.12version>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
        dependency>

第二步:application.yml文件中注入Thymeleaf相关配置,默认只要我们把HTML页面放在classpath:/templates/,thymeleaf就能自动渲染。


server:
  port: 81

logging:
  level:
    com.cbitedu.springboot: debug
    org.springframework.web: debug

spring:
  datasource:
    url:  jdbc:mysql://localhost:3306/platform?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    username:  root
    password:  root
    driver-class-name:  com.mysql.cj.jdbc.Driver
    
    thymeleaf:
      
      prefix: classpath:/templates/
      
      check-template-location: true
      
      cache: false
      
      suffix: .html
      encoding: UTF-8
      mode: HTML5
    devtools:
      restart:
        enabled: true  
        additional-paths: src/main/java 
        

第三步:创建实体类(com.cbitedu.springboot.entity.User)

package com.cbitedu.springboot.entity;

import lombok.Data;
import javax.persistence.*;

@Data
@Entity(name = "t_sys_userinfo")
public class User {

    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Long id;
    @Column(name = "username")
    private String userName;
    @Column(name = "password")
    private String userPwd;
}

持久化类(com.cbitedu.springboot.daoUserRepository )

package com.cbitedu.springboot.dao;


import com.cbitedu.springboot.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository {

    User findByUserName(String userName);
}

服务类(接口类、实现类)UserService 接口

package com.cbitedu.springboot.service;

import com.cbitedu.springboot.entity.User;

public interface UserService {

    User findByUserName(String userName);
}
package com.cbitedu.springboot.service.impl;


import com.cbitedu.springboot.dao.UserRepository;
import com.cbitedu.springboot.entity.User;
import com.cbitedu.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public User findByUserName(String userName) {
        return userRepository.findByUserName(userName);
    }
}

第四步:Apache Shiro配置类UserRealm

package com.cbitedu.springboot.realm;


import com.cbitedu.springboot.entity.User;
import com.cbitedu.springboot.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

@Slf4j
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        log.info("-------------------------------> 认证");
        String userName = (String) token.getPrincipal();
        User user = userService.findByUserName(userName);
        if (user == null) {
            throw new UnknownAccountException();
        }
        String salt = "sdfnegaf7gafj3nfdsfdsj9"; 
        return new SimpleAuthenticationInfo(user, user.getUserPwd(), ByteSource.Util.bytes(salt), getName());
    }
}

第五步:启用Apache Shiro配置类ShiroConfiguration

package com.cbitedu.springboot.config;

import com.cbitedu.springboot.realm.UserRealm;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.*;

@Slf4j
@Configuration
public class ShiroConfiguration {

    
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        log.info("加载 shiro");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setSuccessUrl("/");
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");

        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/**", "user");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        
        securityManager.setRealm(getUserRealm());
        return securityManager;
    }

    
    @Bean
    public Realm getUserRealm() {
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return userRealm;
    }

    
    
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");
        hashedCredentialsMatcher.setHashIterations(2);
        return hashedCredentialsMatcher;
    }

    
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;

    }

    
    
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    
    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
}
第六步,页面控制类 LoginController
package com.cbitedu.springboot.controller;


import com.cbitedu.springboot.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Slf4j
@Controller
public class LoginController {

    @GetMapping(value = {"/", "/index"})
    public String index() {
        return "login";
    }

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

    @PostMapping("/login")
    public String login(User user, Model model) {
        log.info("用户名;" + user.getUserName() + ", 密码:" + user.getUserPwd());
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getUserPwd());
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
            return "index";

        } catch (DisabledAccountException e) {
            model.addAttribute("message", "该用户未授权");
            return "login";
        } catch (UnknownAccountException e) {
            model.addAttribute("message", "该用户不存在");
            return "login";
        } catch (IncorrectCredentialsException e) {
            model.addAttribute("message", "密码错误");
            return "login";
        }
    }
}

第七步:创建登录表单

我们在 resource/templates 目录下新建书籍列表页面 login.html,index.html代码如下:

login.html代码如下:

html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0 ,user-scalable=no">
    <title>登录title>
    
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <link rel="stylesheet" href="css/login.css">
    <link rel="stylesheet" href="iconfont/icon.css">
head>

<body>
<div class="form-box">
    <i class="iconfont icon-U">i>
    <form  action="/login" method="post">
        <h2>Loginh2>
        <label for="user">姓名label>
        
        <input type="text" name="userName" id="user" value="admin" required />
        <label for="password">密码label>
        <input type="password" name="userPwd" id="password" value="123456" required/>

        <p>
            <input type="checkbox" name="check" id="check">
            <label for="check">记住密码label>
        p>
        <input type="submit" value="登录">
        <p class="toregister">没有账号?<a href="#">去注册a>p>
    form>
div>
body>

html>

index.html源代码如下:

html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
<h2 style="color: green">登录成功h2>
body>
html>

此时我们启动项目,然后访问 http://localhost:81/login ,账号:admin/123456

切换不同用户验证结果。

小结

Spring Data JPA

Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!
spring data jpa让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现

实验实训

1、重点学习Apache Shiro其他使用

2、熟练Spring Data JPA使用

阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.shuli.cc/?p=20880,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?