IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringBoot入门级教程(三):SpringData(JDBCDruidMybatis)整合,SpringSecurity和Shiro -> 正文阅读

[Java知识库]SpringBoot入门级教程(三):SpringData(JDBCDruidMybatis)整合,SpringSecurity和Shiro

Spring Data

springboot不管是关联SQL或者是NoSQL数据库,都是以data的形势

整合JDBC

  • 创建项目

在这里插入图片描述

记得添加JDBC API和MySQL Driver的依赖,还有WEB依赖,如果在创建的时候没有添加,也可以在pom中添加,不过尽量不要以这种方式,web依赖最好在创建项目的时候添加,以免发生一些web相关的bug

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
  • 使用idea关联数据库

  • 进行数据库相关配置

    spring:
      datasource:
        username: root
        password: 数据库密码
        url: jdbc:mysql://localhost:3306/count_db?serverTimezone=UTC&userUnicode=true&characterEncoding=utf-8
        driver-class-name: com.mysql.cj.jdbc.Driver
    
  • 自动装配dataSource,测试springBoot默认的数据源

    package com.michilay;
    
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import javax.sql.DataSource;
    
    @SpringBootTest
    class Springboot04DataApplicationTests {
    
    
        private DataSource dataSource;
        @Autowired
        public Springboot04DataApplicationTests(DataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        @Test
        void contextLoads() {
    //        查看默认的数据源
            System.out.println(dataSource.getClass());
            Connection connection = dataSource.getConnection();
            System.out.println(connection);
            connection.close();
        }
    
    }
    

    class com.zaxxer.hikari.HikariDataSource
    2021-08-23 10:01:46.649 INFO 2752 — [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting…
    2021-08-23 10:01:48.858 INFO 2752 — [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
    HikariProxyConnection@1582330795 wrapping com.mysql.cj.jdbc.ConnectionImpl@3c4262d1
    2021-08-23 10:01:48.965 INFO 2752 — [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated…
    2021-08-23 10:01:48.973 INFO 2752 — [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.

    hikari是data默认数据源,和dbcp,速度最快

  • jar包下XXXXTemplate表示就是就是SpringBoot已经帮我们配置好的模板bean,CRUD方法封装在里面,直接拿过来用就可以

    例如我们要使用JDBC的数据源,找到Maven下的spring-boot-autoconfigure-2.3.7.RELEASE.jar,进入org找到JDBC包,找到JdbcTemplateConfiguration类,可以看到以下代码

    @Configuration(
        proxyBeanMethods = false
    )
    //只会有一个Bean生效
    @ConditionalOnMissingBean({JdbcOperations.class})
    class JdbcTemplateConfiguration {
        JdbcTemplateConfiguration() {
        }
    //已经注册了Bean可以直接使用
        @Bean
        @Primary
        //只需要数据源和相关配置,数据源springboot帮忙做了,配置上文也进行了
        JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {
            JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
            Template template = properties.getTemplate();
            jdbcTemplate.setFetchSize(template.getFetchSize());
            jdbcTemplate.setMaxRows(template.getMaxRows());
            if (template.getQueryTimeout() != null) {
                jdbcTemplate.setQueryTimeout((int)template.getQueryTimeout().getSeconds());
            }
    
            return jdbcTemplate;
        }
    }
    

    我们可以直接使用这个Bean进行操作就可以了

  • 使用原生JDBC借助springboot封装的方法进行crud的操作

    package com.michilay.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.List;
    import java.util.Map;
    
    @RestController
    public class JdbcController {
    
        @RequestMapping("/t1")
        public String test(){
            return "hello springboot";
        }
        @Autowired
        JdbcTemplate jdbcTemplate;
    //  查询
        @GetMapping("/userList")
        public List<Map<String,Object>> userList(){
            String sql = "select * from count_db.account";
            List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
            return maps;
        }
    //  添加
        @GetMapping("/addUser")
        public String addUser(){
            String sql = "insert into count_db.account(userId,userName,userPwd,money) values (2,'123123','123123',123.1)";
            jdbcTemplate.update(sql);
    //        自动提交事务
            return "add OK!";
        }
    
        @GetMapping("/updateUser/{id}")
        public String updateUser(@PathVariable("id") int id){
            String sql = "update count_db.account set userName =?,userPwd=?,money=?  where userId=" + id;
            Object[] objects = new Object[3];
            objects[0] = "mcly111";
            objects[1] = "mcly1111";
            objects[2] = "123.123";
            jdbcTemplate.update(sql,objects);
            return "update OK!";
        }
    
        @GetMapping("/deleteUser/{id}")
        public String deleteUser(@PathVariable("id") int id){
            String sql = "delete from count_db.account where userId = ?";
            jdbcTemplate.update(sql,id);
            return "delete OK!";
        }
    }
    

整合Druid

Druid介绍

druid是阿里巴巴开源平台上一个数据库链接池的实现,结合了C3P0、DBCP、PROXOOL等DB池的优点,并且加入了日志监控

Druid使用

  • 导入Druid依赖

    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.6</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.6</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  • 配置数据源

    # 数据源配置
    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    
  • 测试查看数据源

    class com.alibaba.druid.pool.DruidDataSource
    2021-08-23 11:26:03.089 INFO 14580 — [ main] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
    com.mysql.cj.jdbc.ConnectionImpl@608bc8f8

  • 配置Druid专属配置

    spring:
      datasource:
        username: root
        password: 数据库密码
        url: jdbc:mysql://localhost:3306/count_db?serverTimezone=UTC&userUnicode=true&characterEncoding=utf-8
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
    
    
        initialSize: 5
        minIdle: 5
        maxActive: 20
        maxWait: 6000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        poolPreparedStatements: true
        filters: stat,wall,log4j
        maxPoolPreparedStatementPerConnectionSize: 20
        userGlobalDataSourceStat: true
        connectionProperties: druid.stat.mergeSql=true.druid.stat.slowSqlMillis=500
    
  • 编写Druid配置类

    package com.michilay.config;
    
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.support.http.StatViewServlet;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.web.servlet.ServletRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    
    @Configuration
    public class DruidConfig {
    
        @ConfigurationProperties(prefix = "spring.datasource")
        @Bean
        public DataSource druidDataSource(){
            return new DruidDataSource();
        }
    //    后台监控
        @Bean
        public ServletRegistrationBean statViewServlet(){
            ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
    //        后台需要有人登录账号密码
            HashMap<String, String> initParameters = new HashMap<>();
    //        用户名密码的值固定
            initParameters.put("loginUsername","admin");
            initParameters.put("loginPassword","123");
    //        允许谁可以访问
            initParameters.put("allow","");
    
    
    
    //        初始化参数
            bean.setInitParameters(initParameters);
            return bean;
        }
    
    
    }
    
  • 运行服务器,浏览器输入

    http://localhost:8080/druid/sql.html

整合Mybatis

  • 创建springboot项目,导入mybatis、web、jdbc、mysql等依赖

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.4</version>
    </dependency>
    
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    
  • 配置相关信息

    # 应用名称
    spring.application.name=springboot-05-mybatis
    # 数据库驱动:
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    # 数据源名称
    spring.datasource.name=defaultDataSource
    # 数据库连接地址
    spring.datasource.url=jdbc:mysql://localhost:3306/count_db?serverTimezone=UTC&userUnicode=true&characterEncoding=utf-8
    # 数据库用户名&密码:
    spring.datasource.username=root
    spring.datasource.password=数据库密码
    #下面这些内容是为了让MyBatis映射
    #指定Mybatis的Mapper文件
    mybatis.mapper-locations=classpath:mapper/*.xml
    #指定Mybatis的实体目录
    mybatis.type-aliases-package=com.michilay.pojo
    # 应用服务 WEB 访问端口
    server.port=8080
    
  • 编写实体类和对应的mapper接口

    package com.michilay.pojo;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import org.springframework.stereotype.Component;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private int userId;
        private String userName;
        private String userPwd;
        private Double money;
    }
    
    package com.michilay.mapper;
    
    
    import com.michilay.pojo.User;
    import org.apache.ibatis.annotations.Mapper;
    import org.springframework.stereotype.Repository;
    
    import java.util.List;
    
    @Mapper
    @Repository
    public interface UserMapper {
    
        List<User> findAll();
        User findUserById(int id);
        int addUser(User user);
        int updateUser(User user);
        int deleteUser(int id);
    }
    
  • 在编写对应的service层和实现类

    package com.michilay.service;
    
    import com.michilay.pojo.User;
    
    import java.util.List;
    
    public interface UserService {
        List<User> findAll();
        User findUserById(int id);
        int addUser(User user);
        int updateUser(User user);
        int deleteUser(int id);
    }
    
    package com.michilay.service;
    
    import com.michilay.mapper.UserMapper;
    import com.michilay.pojo.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    
    @Service("userService")
    public class UserServiceImpl implements UserService{
    
        UserMapper userMapper;
        @Autowired
        public UserServiceImpl(UserMapper userMapper) {
            this.userMapper = userMapper;
        }
    
        @Override
        public List<User> findAll() {
            return userMapper.findAll();
        }
    
        @Override
        public User findUserById(int id) {
            return userMapper.findUserById(id);
        }
    
        @Override
        public int addUser(User user) {
            return userMapper.addUser(user);
        }
    
        @Override
        public int updateUser(User user) {
            return userMapper.updateUser(user);
        }
    
        @Override
        public int deleteUser(int id) {
            return userMapper.deleteUser(id);
        }
    }
    
  • 编写实现增删改查的mapper.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.michilay.mapper.UserMapper">
    
        <select id="findAll" resultType="User">
            select * from count_db.account
        </select>
    
        <select id="findUserById" resultType="User">
            select * from count_db.account where userId=#{userId}
        </select>
    
        <insert id="addUser" parameterType="User">
            insert into count_db.account(userName, userPwd, money) values (#{userName},#{userPwd},#{money})
        </insert>
    
        <update id="updateUser" parameterType="User">
            update count_db.account set userName=#{userName},userPwd=#{userPwd},money=#{money} where userId=#{userId}
        </update>
    
        <delete id="deleteUser" parameterType="int">
            delete from count_db.account where userId=#{userId}
        </delete>
    
    </mapper>
    

    记得放在正确的路径下面

  • 编写对应的controller层

    package com.michilay.controller;
    
    
    import com.michilay.pojo.User;
    import com.michilay.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.List;
    
    @RestController
    public class UserController {
    
        UserService userService;
        @Autowired
        public UserController(UserService userService) {
            this.userService = userService;
        }
    
        @GetMapping("/findAll")
        public List<User> findAll(){
            List<User> all = userService.findAll();
            return all;
        }
        @GetMapping("/addUser")
        public String addUser(){
            User user = new User();
            user.setUserName("boot");
            user.setUserPwd("213asd");
            user.setMoney(123D);
            userService.addUser(user);
            return "添加成功";
        }
        @GetMapping("/updateUser")
        public String updateUser(){
            User user = new User();
            user.setUserId(1);
            user.setMoney(1231232131232D);
            userService.updateUser(user);
            return "修改成功";
        }
        @GetMapping("/deleteUser")
        public String deleteUser(){
            userService.deleteUser(1);
            return "删除成功";
        }
    }
    
  • 运行程序进行测试,查看是否对数据库修改有作用

SpringSecurity

spring security是针对spring项目的安全管理框架,是针对于底层的安全模块默认的技术选型,它可以实现web安全控制,可以进行认证和授权

它是一种非功能性需求,安全问题是在网站从底层设计开始到后期维护的过程中一直要考虑的

项目构建

  • 创建springboot项目,导入thymeleaf和web依赖

  • 写几个静态网页

process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_Q1NETiBAbWljaGlsYXk=,size_23,color_FFFFFF,t_70,g_se,x_16)

主页可以进入登陆页面,也可以通过主页进入三个等级的共九个页面

  • 控制层代码如下

    package com.michilay.controller;
    
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class RouterController {
    
        @RequestMapping({"/", "/index"})
        public String index(){
            return "index";
        }
        @RequestMapping("toLogin")
        public String login(){
            return "views/login";
        }
        @RequestMapping("/level1/{id}")
        public String level1(@PathVariable("id") int id){
            return "views/level1/"+id;
        }
        @RequestMapping("/level2/{id}")
        public String level2(@PathVariable("id") int id){
            return "views/level2/"+id;
        }
        @RequestMapping("/level3/{id}")
        public String level3(@PathVariable("id") int id){
            return "views/level3/"+id;
        }
    }
    

用户认证和授权

  • 导入SpringSecurity的依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
  • 写一个配置类继承WebSecurityConfigurerAdapter,并且加上@EnableWebSecurity注释表示被spring托管了

    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            super.configure(http);
        }
    }
    
  • 更改方法体如下

    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //    链式编程
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    //        首页所有人都可以访问,功能页必须要有权限的人才能访问
            http.authorizeRequests().antMatchers("/").permitAll()
                    .antMatchers("/level1/**").hasRole("vip1")
                    .antMatchers("/level2/**").hasRole("vip2")
                    .antMatchers("/level3/**").hasRole("vip3");
        }
    }
    
  • 重启服务器,再次打开主页面,当我们点进相关页面时,会显示type=Forbidden, status=403报错

  • 但是我们不要报错,如果没有权限的话要跳转的登录页面

    //        没有权限默认到登录页
            http.formLogin();
    

    这样在我们没有权限,点击level的时候就会跳转到一个login页面,这个页面是secruity的页面

  • 那么,那些用户拥有这些等级呢,这里先用内存创建用户,再重写一个方法

    //      认证
    //    在spring security5.0+新增了很多机密方式
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    //        从内存中读取
            auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                    .withUser("michilay").password(new BCryptPasswordEncoder().encode("123")).roles("vip2","vip1")
                    .and()
                    .withUser("root").password(new BCryptPasswordEncoder().encode("123")).roles("vip1","vip2","vip3")
                    .and()
                    .withUser("guest").password(new BCryptPasswordEncoder().encode("123")).roles("vip1");
        }
    

    注意:必须得用加密,不然登录会出现问题

注销和权限控制

  • 写一个注销按钮,跳转到注销请求,这些请求都是spring security封装好的,我们也可以把登陆和security自带的登录页绑定

在这里插入图片描述

  • 开启注销功能

    //        没有权限默认到登录页
            http.formLogin();
    //        开启了注销功能,并且注销后跳转到首页
            http.logout().logoutSuccessUrl("/");
    
  • 如果要开启权限显示的功能,需要导入thymeleaf和security整合包

    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    </dependency>
    
  • 在首页的上方导入命名空间

    <html lang="en" xmlns:th="http://www.thymeleaf.org"
          xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
    
  • 通过sec命名空间结合security判断是否登录

    <!--                如果未登录-->
                    <div sec:authorize="!isAuthenticated()">
                        <a class="item" th:href="@{/login}">
                            <i class="sign-in icon"></i> 登录
                        </a>
                    </div>
    <!--                如果以登录,注销-->
                    <div sec:authorize="isAuthenticated()" style="display:flex; flex-direction:row;">
                        <a class="item">
                            用户名:<span sec:authentication="name"></span>&nbsp;&nbsp;&nbsp;&nbsp;
                            等级:<span sec:authentication="principal.authorities"></span>
                        </a>
                        <a class="item" th:href="@{/logout}">
                            <i class="sign-out icon"></i> 注销
                        </a>
                    </div>
    
  • 如果登出失败,关闭scrf功能

    //        关闭csrf功能
            http.csrf().disable();
    
  • sec也可以通过权限来判断,根据结果是否显示

        <div>
    <!--            没有登陆的时候显示,登陆了就不显示-->      
            <div class="ui segment" style="text-align: center" sec:authorize="!isAuthenticated()">
                <h4>您还没有登录,请登录</h4>
            </div>
    
            <div class="ui three column stackable grid">
    <!--            vip1的时候显示-->
                <div class="column" sec:authorize="hasRole('vip1')">
                    <div class="ui raised segment">
                        <div class="ui">
                            <div class="content">
                                <h5 class="content">Level 1</h5>
                                <hr>
                                <div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
                                <div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
                                <div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
                            </div>
                        </div>
                    </div>
                </div>
    
                <div class="column" sec:authorize="hasRole('vip2')">
                    <div class="ui raised segment">
                        <div class="ui">
                            <div class="content">
                                <h5 class="content">Level 2</h5>
                                <hr>
                                <div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
                                <div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
                                <div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
                            </div>
                        </div>
                    </div>
                </div>
    
                <div class="column" sec:authorize="hasRole('vip3')">
                    <div class="ui raised segment">
                        <div class="ui">
                            <div class="content">
                                <h5 class="content">Level 3</h5>
                                <hr>
                                <div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
                                <div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
                                <div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
                            </div>
                        </div>
                    </div>
                </div>
    
            </div>
        </div>
    

首页定制以及记住我功能

记住我的功能比较简单

  • 开启记住我功能

    //        开启记住我功能
            http.rememberMe();
    
  • 在登陆的时候,security登录页面会出现记住我的选项,如果选上,在下一次进入该网页时会自动使用cookies

    持续两周的时间

  • 可以使用浏览器清除cookie,重启后登录信息失效,因为浏览器每次启动都发起一次会话,cookie等数据已经发送到服务器中

配置自定义登陆页面

  • 配置跳转到登陆页

    //        没有权限默认到登录页
            http.formLogin().loginPage("/toLogin");
    

    这里使用自己的登陆页面,toLogin是已经写好的登录跳转请求

  • 写一个登陆页面

    <form th:action="@{/login}" method="post">
        <div class="field">
            <label>Username</label>
            <div class="ui left icon input">
                <input type="text" placeholder="Username" name="username">
                <i class="user icon"></i>
            </div>
        </div>
        <div class="field">
            <label>Password</label>
            <div class="ui left icon input">
                <input type="password" name="password">
                <i class="lock icon"></i>
            </div>
        </div>
        <div class="field">
            <input type="checkbox" name="remember" style="align-content: center">记住我
        </div>
        <input type="submit" class="ui blue submit button"/>
    </form>
    
  • 这里登录并不能登录成功,因为接收找不到/login请求,会报404错误

  • 必须要将跳转页面加上一个登陆页面里的对应请求

    http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login");
    
  • 但是它是怎么和我们自己写的页面的username和password对应的呢?看源码

    usernameParameter("username") // default is username
    passwordParameter("password") // default is password
    
  • 看来是formLogin这个方法自动帮我们识别了啊,但是如果我们写的网站name不是标准的username和password呢?假如是name和pwd这两个属性,进行如下修改即可

    http.formLogin().loginPage("/toLogin").usernameParameter("name").passwordParameter("pwd").loginProcessingUrl("/login");
    
  • 记住我的绑定也和用户名密码差不多,但是它没有默认值,必须要对应

    //        开启记住我功能
            http.rememberMe().rememberMeParameter("remember");
    

SpringSecurity总结

  • 优点:

    springSecurity和shiro很相似,都是安全框架,学完了之后,最大的感触就是,之前的ssm框架中,登录从数据库查找登陆验证,需要AOP横切进去功能,即使用了相关的方法但是感觉还是很复杂,而用了安全框架之后,只需要重写对应的方法,进行自定义话的设计即可,管理起来也很方便,数据库可以设计的更加简单了

  • 使用方式步骤:

    1. 导入spring-boot-starter-securitythymeleaf-extras-springsecurity5依赖

    2. 写一个配置类继承WebSecurityConfigurerAdapter,并且加上@EnableWebSecurity注释

    3. 如果要进行认证就重写configure(AuthenticationManagerBuilder auth)方法,如果要进行权限管理,登陆页面管理就重写configure(HttpSecurity http)方法

    4. 后台调用http对象的方法设置权限,登录页,注销,csrf和记住我等功能

      //        首页所有人都可以访问,功能页必须要有权限的人才能访问
              http.authorizeRequests().antMatchers("/").permitAll()
                      .antMatchers("/level1/**").hasRole("vip1")
                      .antMatchers("/level2/**").hasRole("vip2")
                      .antMatchers("/level3/**").hasRole("vip3");
      //        没有权限默认到登录页
              http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login");
      //        开启了注销功能,并且注销后跳转到首页
              http.logout().logoutSuccessUrl("/");
      //        关闭csrf功能
              http.csrf().disable();
      //        开启记住我功能
              http.rememberMe().rememberMeParameter("remember");
      
    5. 前端引入thyleaf和security命名空间

      <xmlns:th="http://www.thymeleaf.org"
            xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
      
    6. sec:authorize="isAuthenticated()"来判断是否登录,sec:authentication="name"可以拿到登录用户名,sec:authentication="principal.authorities"可以拿到登录用户的权限等级

    7. 如果使用usernamepassword则为默认不用绑定,如果不是则需要绑定,remember一定要通过rememberMeParameter("remember")这个方法绑定

Shiro

Shiro概述

shiro是Apache的一个java安全权限框架,它可以非常容易开发出足够好的应用,不仅可以用在javaSE环境,也可以应用在javaEE环境(包含自己的session)

shiro可以完成认证、授权、加密、会话管理、web集成、缓存等操作

Shiro十分钟快速开发

快速开发可以参考git项目shiro-root-1.7.1/samples/quickstart/该目录下

  • 创建一个新的maven项目,删掉src,创建一个新的模块,导入相关依赖

    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.7.1</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>1.5.6</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.30</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  • 配置log4j,创建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
    
  • 配置shiro,创建shiro.ini

    [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]
    # '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
    
  • 导入shiro的java文件

    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) {
    
            // The easiest way to create a Shiro SecurityManager with configured
            // realms, users, roles and permissions is to use the simple INI config.
            // We'll do that by using a factory that can ingest a .ini file and
            // return a SecurityManager instance:
    
            // Use the shiro.ini file at the root of the classpath
            // (file: and url: prefixes load from files and urls respectively):
            Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            SecurityManager securityManager = factory.getInstance();
    
            // for this simple example quickstart, make the SecurityManager
            // accessible as a JVM singleton.  Most applications wouldn't do this
            // and instead rely on their container configuration or web.xml for
            // webapps.  That is outside the scope of this simple quickstart, so
            // we'll just do the bare minimum so you can continue to get a feel
            // for things.
            SecurityUtils.setSecurityManager(securityManager);
    
            // Now that a simple Shiro environment is set up, let's see what you can do:
    
            // get the currently executing user:
            Subject currentUser = SecurityUtils.getSubject();
    
            // Do some stuff with a Session (no need for a web or EJB container!!!)
            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 + "]");
            }
    
            // let's login the current user so we can check against roles and permissions:
            if (!currentUser.isAuthenticated()) {
                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 more exceptions here (maybe custom ones specific to your application?
                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.");
    
            //test a role:
            if (currentUser.hasRole("schwartz")) {
                log.info("May the Schwartz be with you!");
            } else {
                log.info("Hello, mere mortal.");
            }
    
            //test a typed permission (not instance-level)
            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);
        }
    }
    

    可以看出写这段代码的人是个星战爱好者

    1. 账号: 孤独的斯塔尔(Lone Starr) 进行登陆操作!
    2. 如果拥有角色:Schwartz(应该原力)
    3. 登陆成功提示:愿原力与你同在!
    4. 登陆失败提示:你好,凡人(无原力者)
    5. 如果有此权限,提示:你可以使用光剑,用它秀吧!
    6. 如果没有此权限:抱歉,光剑是绝地武士专属武器
    7. 最后一个例子: winnebago (lone starr 的飞船名字) eagle5=也许是船长lone starr的驾驶员代号?
    8. 如果有此权限:你可以使用老鹰5号作为驾驶通行证,给你钥匙!
  • 运行,得到如下信息即可

    2021-08-24 16:04:20,494 INFO [org.apache.shiro.session.mgt.AbstractValidatingSessionManager] - Enabling session validation scheduler…
    2021-08-24 16:04:21,728 INFO [Quickstart] - Retrieved the correct value! [aValue]
    2021-08-24 16:04:21,729 INFO [Quickstart] - User [lonestarr] logged in successfully.
    2021-08-24 16:04:21,729 INFO [Quickstart] - May the Schwartz be with you!
    2021-08-24 16:04:21,730 INFO [Quickstart] - You may use a lightsaber ring. Use it wisely.
    2021-08-24 16:04:21,730 INFO [Quickstart] - You are permitted to ‘drive’ the winnebago with license plate (id) ‘eagle5’. Here are the keys - have fun!

    进程已结束,退出代码为 0

Shiro快速开发的原理

public class Quickstart {
//    使用日志输出
    private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
    public static void main(String[] args) {
//        通过工厂读取配置文件,创建实例
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();
//        单例,全局唯一的对象,可以不用配置
        SecurityUtils.setSecurityManager(securityManager);

//        通过SecurityUtils获取当前的用户对象subject
        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("Subject存在session的值为[" + value + "]");
        }

//        测试当前的用户是否被认证
        if (!currentUser.isAuthenticated()) {
//            没有被认证,创建一个令牌,该用户是在ini文件里存在的
            UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
//            开启该令牌的记住我功能
            token.setRememberMe(true);
            try {
//                执行了登录操作
                currentUser.login(token);
            } catch (UnknownAccountException uae) {
                log.info("该用户名不正确 " + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) {
                log.info("用户" + token.getPrincipal() + " 不正确");
            } catch (LockedAccountException lae) {
                log.info("该账户的用户名" + token.getPrincipal() + "被锁定了" +
                        "请联系您的管理员解锁");
            }
            // ... 在这里捕获更多异常 (也许定制的特定于您的应用程序?
            catch (AuthenticationException ae) {
                //意外情况?错误?
            }
        }

        //打印其识别主体(在本例中为用户名)
        log.info("用户 [" + currentUser.getPrincipal() + "] 成功登陆了");

        //测试角色,是否有这个role
        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);
    }
}

总结下来就是以下代码

//       0.通过工厂读取配置文件,创建实例
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();
//        单例,全局唯一的对象,可以不用配置
        SecurityUtils.setSecurityManager(securityManager);
//        1.通过SecurityUtils获取当前的用户对象subject
        Subject currentUser = SecurityUtils.getSubject();
//        3.通过当前用户拿到session,存值并且取值
        Session session = currentUser.getSession();
//        3.1测试当前的用户是否被认证
        if (!currentUser.isAuthenticated())
//         3.1没有被认证,创建一个令牌,该用户是在ini文件里存在的
            UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
//                4.执行了登录操作
                currentUser.login(token);
//打印其识别主体(在本例中为用户名)
log.info("用户 [" + currentUser.getPrincipal() + "] 成功登陆了");
是否有这个role
if (currentUser.hasRole("schwartz")) {
//        粗粒度
        if (currentUser.isPermitted("lightsaber:wield"))
//        细粒度
        if (currentUser.isPermitted("winnebago:drive:eagle5")) 
//5.登出
currentUser.logout();

整体下来,发现其实和springSecurity很像

Shiro整合Springboot

  • 创建springboot项目,导入web依赖和thymeleaf框架

  • 测试thymeleaf的th:text标签和controller是否能跑成功

  • 没问题之后导入shiro相关依赖

    <!--        subject:用户
                shiro securityManager:管理所有用户
                realm:连接数据-->
    <dependency>
       
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.7.1</version>
    </dependency>
    
  • 编写UserRealm授权登录类

    package com.michilay.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;
    
    //自定义Realm继承authorizingRealm
    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;
        }
    }
    

    继承AuthorizingRealm实现授权和认证方法方法

  • 编写ShiroConfig配置类

    package com.michilay.config;
    
    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 ShiroConfig {
    //    1.ShiroFilterFactoryBean
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    //        设置安全管理器
            shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
            return shiroFilterFactoryBean;
        }
    //    2.DefaultWebSecurityManager
        @Bean
        public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    //        关联realm
            securityManager.setRealm(userRealm);
            return securityManager;
        }
    //    1.创建realm对象,需要自定义
        @Bean
        public UserRealm userRealm(){
            return new UserRealm();
        }
    }
    
    1. 代码从下往上编写,先创建realm对象,注册bean,先把自定义的类拿过来交给spring托管
    2. 中间商DefaultWebSecurityManager管理realm对象,并且也交给spring托管
    3. ShiroFilterFactoryBean工厂设置安全管理器,注册bean
  • 写主页进行跳转

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>主页</h1>
        <p th:text="${msg}"></p>
        <hr>
        <a th:href="@{/user/add}">add</a>
        <a th:href="@{/user/update}">update</a>
    </body>
    </html>
    
  • controller

    package com.michilay.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class MyController {
    
        @RequestMapping({"/", "/index"})
        public String toIndex(Model model){
            model.addAttribute("msg","hello,shiro");
            return "index";
        }
        @RequestMapping("user/add")
        public String toAdd(){
            return "user/add";
        }
        @RequestMapping("user/update")
        public String toUpdate(){
            return "user/update";
        }
    
    }
    

登录拦截

  • 添加过滤器

    //    1.ShiroFilterFactoryBean
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
            ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
    //        设置安全管理器
            bean.setSecurityManager(defaultWebSecurityManager);
    //        anon:无需认证就可以访问,authc:必须认证了才能访问,user:必须拥有记住我功能才能用
    //        perms:拥有对某个资源的权限才能访问,role:拥有某个角色权限才能访问
            HashMap<String, String> filterMap = new LinkedHashMap<>();
            filterMap.put("/user/add","authc");
            filterMap.put("/user/update","authc");
            bean.setFilterChainDefinitionMap(filterMap);
    //        设置登录请求
            bean.setLoginUrl("/toLogin");
    
            return bean;
        }
    
  • 写一个登录页面和请求

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>登录</h1>
        <form th:action="@{/login}">
          <p>用户名: <input type="text" name="username"></p>
          <p>用户名: <input type="password" name="password"></p>
            <input type="submit">
        </form>
    </body>
    </html>
    
    @RequestMapping("/toLogin")
    public String toLogin(){
        return "login";
    }
    

用户认证

  • 写登录请求

        @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);
                return "index";
            } catch (UnknownAccountException e) {
                model.addAttribute("msg","你还未注册,请先注册");
                return "login";
            }catch (IncorrectCredentialsException e) {
                model.addAttribute("msg","密码错误");
                return "login";
            }h
    
  • 在UserRealm封装用户信息并且进行认证

    //   认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            System.out.println("执行了认证");
    
    //        用户名,密码 从数据中去
            String name="admin";
            String password="123";
            UsernamePasswordToken userToken=(UsernamePasswordToken) token;
            if (!userToken.getUsername().equals(name)){
    //            抛出异常UnknownAccountException e
                return null;
            }
    //        密码认证,shiro来做
            return new SimpleAuthenticationInfo("",password,"");
        }
    

Shiro大整合

在之前的项目里增增改改依赖,感觉会增多很多bug出现的频率,这边整合了之前学习的内容,结合如下技术进行一个总结学习

项目搭建

数据库mysql,持久层框架mybatis,数据源druid,前端引擎thymeleaf

  • 创建springboot项目,导入web+mysql+mybatis+lombok+thymeleaf依赖,创建后再导入log4j、druid和shiro依赖

    完整pom如下

    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.7.1</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.6</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.4</version>
    </dependency>
    
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    
  • application.properties配置

    # 应用名称
    spring.application.name=springboot-09-shiro-mybatis-druid
    # 应用服务 WEB 访问端口
    server.port=8080
    #下面这些内容是为了让MyBatis映射
    #指定Mybatis的Mapper文件
    mybatis.mapper-locations=classpath:mapper/*.xml
    #指定Mybatis的实体目录
    mybatis.type-aliases-package=com.michilay.pojo
    # THYMELEAF (ThymeleafAutoConfiguration)
    # 开启模板缓存(默认值: true )
    spring.thymeleaf.cache=true
    # 检查模板是否存在,然后再呈现
    spring.thymeleaf.check-template=true
    # 检查模板位置是否正确(默认值 :true )
    spring.thymeleaf.check-template-location=true
    #Content-Type 的值(默认值: text/html )
    spring.thymeleaf.content-type=text/html
    # 开启 MVC Thymeleaf 视图解析(默认值: true )
    spring.thymeleaf.enabled=true
    # 模板编码
    spring.thymeleaf.encoding=UTF-8
    # 要被排除在解析之外的视图名称列表,?逗号分隔
    spring.thymeleaf.excluded-view-names=
    # 要运?于模板之上的模板模式。另? StandardTemplate-ModeHandlers( 默认值: HTML5)
    spring.thymeleaf.mode=HTML5
    # 在构建 URL 时添加到视图名称前的前缀(默认值: classpath:/templates/ )
    spring.thymeleaf.prefix=classpath:/templates/
    # 在构建 URL 时添加到视图名称后的后缀(默认值: .html )
    spring.thymeleaf.suffix=.html
    
  • application.yaml关于数据源的相关配置

    spring:
      datasource:
        username: root
        password: 数据库密码
        url: jdbc:mysql://localhost:3306/count_db?serverTimezone=UTC&userUnicode=true&characterEncoding=utf-8
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
    
        initialSize: 5
        minIdle: 5
        maxActive: 20
        maxWait: 6000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        poolPreparedStatements: true
        filters: stat,wall,log4j
        maxPoolPreparedStatementPerConnectionSize: 20
        userGlobalDataSourceStat: true
        connectionProperties: druid.stat.mergeSql=true.druid.stat.slowSqlMillis=500
    
  • log4j.properties日志配置

    (idea又崩溃了,我心态炸了)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RIY8PD1k-1629814195982)(C:\Users\Michilay\AppData\Roaming\Typora\typora-user-images\image-20210824203213388.png)]

    如下。。。

    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
    
  • 德鲁伊配置类DruidConfig

    package com.michilay.config;
    
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.support.http.StatViewServlet;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.web.servlet.ServletRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    
    @Configuration
    public class DruidConfig {
    
        @ConfigurationProperties(prefix = "spring.datasource")
        @Bean
        public DataSource druidDataSource(){
            return new DruidDataSource();
        }
        //    后台监控
        @Bean
        public ServletRegistrationBean statViewServlet(){
            ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
    //        后台需要有人登录账号密码
            HashMap<String, String> initParameters = new HashMap<>();
    //        用户名密码的值固定
            initParameters.put("loginUsername","admin");
            initParameters.put("loginPassword","123");
    //        允许谁可以访问
            initParameters.put("allow","");
    
    
    
    //        初始化参数
            bean.setInitParameters(initParameters);
            return bean;
        }
    
    
    }
    
  • 实体类,mapper,service层,service实现代码,对应的mapper.xml一套,这里先写一个方法

    User findUserByName(String userName);
    
    <select id="findUserByName" resultType="User" parameterType="String">
        select * from count_db.account where userName=#{userName}
    </select>
    
  • 测试

    package com.michilay;
    
    import com.michilay.pojo.User;
    import com.michilay.service.UserService;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    @SpringBootTest
    class Springboot09ShiroMybatisDruidApplicationTests {
    
    
        UserService userService;
        @Autowired
        public Springboot09ShiroMybatisDruidApplicationTests(UserService userService) {
            this.userService = userService;
        }
    
        @Test
        void contextLoads() {
            User michilay = userService.findUserByName("michilay");
            System.out.println(michilay);
        }
    
    }
    

    好的没问题进行下一步

  • 将shiro模块的shiroConfig,userRealm以及静态资源导入

登陆验证

  • 现在要用shiro来管理用户登录 UserRealm

    package com.michilay.config;
    
    import com.michilay.pojo.User;
    import com.michilay.service.UserService;
    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.springframework.beans.factory.annotation.Autowired;
    
    //自定义Realm继承authorizingRealm
    public class UserRealm extends AuthorizingRealm {
        @Autowired
        UserService userService;
        //    授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("执行了授权");
            return null;
        }
    //   认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            System.out.println("执行了认证");
    //        连接真实的数据库
            UsernamePasswordToken userToken = (UsernamePasswordToken) token;
            User userByName = userService.findUserByName(userToken.getUsername());
            if (userByName==null){
                return null;
            }
    //        密码加密:md5  md5盐值加密:
    //        密码认证,shiro来做
            return new SimpleAuthenticationInfo("",userByName.getUserPwd(),"");
        }
    }
    

请求授权

  • 写一个未授权请求

    @RequestMapping("/noauth")
    @ResponseBody
    public String unauthorized(){
        return "未经授权无法访问此页面";
    }
    
  • 设置权限过滤功能,ShiroConfig如下

    package com.michilay.config;
    
    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;
    
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    
    @Configuration
    public class ShiroConfig {
    
    //    1.ShiroFilterFactoryBean
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
            ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
    //        设置安全管理器
            bean.setSecurityManager(defaultWebSecurityManager);
    //        anon:无需认证就可以访问,authc:必须认证了才能访问,user:必须拥有记住我功能才能用
    //        perms:拥有对某个资源的权限才能访问,role:拥有某个角色权限才能访问
            HashMap<String, String> filterMap = new LinkedHashMap<>();
    //        正常情况下,没有授权会到未授权页面
    //        只有在包含了add的字符串
            filterMap.put("/user/add","perms[user:add]");
            filterMap.put("/user/update","authc");
            bean.setFilterChainDefinitionMap(filterMap);
    //        设置登录请求
            bean.setLoginUrl("/toLogin");
    //        设置未授权请求
            bean.setUnauthorizedUrl("/noauth");
            return bean;
        }
    //    2.DefaultWebSecurityManager
        @Bean
        public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    //        关联realm
            securityManager.setRealm(userRealm);
            return securityManager;
        }
    //    1.创建realm对象,需要自定义
        @Bean
        public UserRealm userRealm(){
            return new UserRealm();
        }
    }
    

    这样登录add页面必须要求user又add字段才可以

  • 我们在UserRealm设置权限

    //    授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("执行了授权");
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    //        给user增加add的字符串
            info.addStringPermission("user:add");
            return info;
        }
    

    这里给所有的用户都授权,也就是登录增加add权限,实现权限管理功能

  • 但是这并不是我们想要的,肯定要区分那些用户有那些权限,这时候就要更改数据库表,增加一个权限字段,修改部分权限

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pAsSInj8-1629814195984)(C:\Users\Michilay\AppData\Roaming\Typora\typora-user-images\image-20210824214847603.png)]

  • 更改实体类

  • 权限访问更改如下

    filterMap.put("/user/add","perms[user:add]");
    filterMap.put("/user/update","perms[user:update]");
    
  • 授权如下,直接从数据库里拿到权限信息

        //    授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    //        拿到当前登录用户对象
            Subject subject = SecurityUtils.getSubject();
    //        认证中        return new SimpleAuthenticationInfo(userByName,userByName.getUserPwd(),"");得到了userByName这个user
    //        这里就可以拿到user
            User currentUser = (User) subject.getPrincipal();
    //        设置权限
            info.addStringPermission(currentUser.getPerms());
            return info;
        }
    

权限分级展示

结合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>
    
  • 在ShiroConfig里配置注册

    //    整合ShiroDialect
        @Bean
        public ShiroDialect getShiroDialect(){
            return  new ShiroDialect();
        }
    
  • 前端页面

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org"
          xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>主页</h1>
        <p th:text="${msg}"></p>
        <div shiro:guest="true">
            <a th:href="@{/toLogin}">登录</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>
    

    进入首页,显示登录超链接,点击登录,登陆后登录超链接消失,显示对应的权限页面,例如有add权限的显示add页面

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-25 12:04:45  更:2021-08-25 12:05:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 9:26:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码