天天看點

springboot + mybatis + gradle項目建構過程

1.從 Spring boot官網

根據需求下載下傳腳手架或者到GitHub上去搜尋對應的腳手架項目,D_iao ^0^

• 檔案目錄如下(此處generatorConfig.xml 和 log4j2.xml檔案請忽略,後續會講解)

springboot + mybatis + gradle項目建構過程

2.使用Mybatis代碼自動建構插件生成代碼

•  gradle 相關配置

// Mybatis 代碼自動生成所引入的包
compile group: 'org.mybatis.generator', name: 'mybatis-generator-core', version: '1.3.3'

// MyBatis代碼自動生成插件工具
apply plugin: "com.arenagod.gradle.MybatisGenerator"

configurations {
    mybatisGenerator
}

mybatisGenerator {
    verbose = true
    // 配置檔案路徑
    configFile = 'src/main/resources/generatorConfig.xml'
}      

•  generatorConfig.xml配置詳解

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

    <!--資料庫驅動包路徑 -->
    <classPathEntry
            <!--此驅動包路徑可在項目的包庫中找到,複制過來即可-->
            location="C:\Users\pc\.gradle\caches\modules-2\files-2.1\mysql\mysql-connector-java\5.1.38\dbbd7cd309ce167ec8367de4e41c63c2c8593cc5\mysql-connector-java-5.1.38.jar"/>

    <context id="mysql" targetRuntime="MyBatis3">
        <!--關閉注釋 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!--資料庫連接配接資訊 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/xxx" userId="root"
                        password="">
        </jdbcConnection>

        <!--生成的model 包路徑 ,其中rootClass為model的基類,配置之後他會自動繼承該類作為基類,trimStrings會為model字串去空格-->
        <javaModelGenerator targetPackage="com.springboot.mybatis.demo.model"
                            targetProject="D:/self-code/spring-boot-mybatis/spring-boot-mybatis/src/main/java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
            <property name="rootClass" value="com.springboot.mybatis.demo.model.common.BaseModel"/>
        </javaModelGenerator>

        <!--生成mapper xml檔案路徑 -->
        <sqlMapGenerator targetPackage="mapper"
                         targetProject="D:/self-code/spring-boot-mybatis/spring-boot-mybatis/src/main/resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>

        <!-- 生成的Mapper接口的路徑 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.springboot.mybatis.demo.mapper" targetProject="D:/self-code/spring-boot-mybatis/spring-boot-mybatis/src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <!-- 對應的表 這個是生成Mapper xml檔案的基礎,enableCountByExample如果為true則會在xml檔案中生成樣例,過于累贅是以不要-->
        <table tableName="tb_user" domainObjectName="User"
               enableCountByExample="false"
               enableDeleteByExample="false"
               enableSelectByExample="false"
               enableUpdateByExample="false"></table>
    </context>


</generatorConfiguration>      

以上配置中注意targetProject路徑請填寫絕對路徑,避免錯誤,其中targetPackage是類所處的包路徑(確定包是存在的,否則無法生成),也就相當于

springboot + mybatis + gradle項目建構過程

•  代碼生成

配置完成之後首先得在資料庫中建立對應的表,然後確定資料庫能正常通路,最後在終端執行gradle mbGenerator或者點選如下任務

springboot + mybatis + gradle項目建構過程

成功之後它會生成model、mapper接口以及xml檔案

3.內建日志

• gradle 相關配置

compile group: 'org.springframework.boot', name: 'spring-boot-starter-log4j2', version: '1.4.0.RELEASE'

// 排除沖突
configurations {
    mybatisGenerator
    compile.exclude module: 'spring-boot-starter-logging'
}      

當沒有引入spring-boot-starter-log4j2包時會報錯:java.lang.IllegalStateException: Logback configuration error detected Logback 配置錯誤聲明

原因參考連結;

https://blog.csdn.net/blueheart20/article/details/78111350?locationNum=5&fps=1

解決方案:排除依賴 spring-boot-starter-logging

what???

排除依賴之後使用的時候又報錯:Failed to load class "org.slf4j.impl.StaticLoggerBinder" 加載slf4j.impl.StaticLoggerBinder類失敗

原因參考連結:

https://blog.csdn.net/lwj_199011/article/details/51853110

解決方案:添加依賴 spring-boot-starter-log4j2 此包所依賴的包如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starters</artifactId>
        <version>1.4.0.RELEASE</version>
    </parent>
    <artifactId>spring-boot-starter-log4j2</artifactId>
    <name>Spring Boot Log4j 2 Starter</name>
    <description>Starter for using Log4j2 for logging. An alternative to
        spring-boot-starter-logging</description>
    <url>http://projects.spring.io/spring-boot/</url>
    <organization>
        <name>Pivotal Software, Inc.</name>
        <url>http://www.spring.io</url>
    </organization>
    <properties>
        <main.basedir>${basedir}/../..</main.basedir>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jul-to-slf4j</artifactId>
        </dependency>
    </dependencies>
</project>      

它依賴了 log4j-slf4j-impl ,使用的是log4j2日志架構。

這裡涉及到log4j、logback、log4j2以及slf4j相關概念,那麼它們是啥關系呢?unbelievable...相關知識如下:

slf4j、log4j、logback、log4j2 

日志接口(slf4j) 
slf4j是對所有日志架構制定的一種規範、标準、接口,并不是一個架構的具體的實作,因為接口并不能獨立使用,需要和具體的日志架構實作配合使用(如log4j、logback)

日志實作(log4j、logback、log4j2)
log4j是apache實作的一個開源日志元件
logback同樣是由log4j的作者設計完成的,擁有更好的特性,用來取代log4j的一個日志架構,是slf4j的原生實作
Log4j2是log4j 1.x和logback的改進版,據說采用了一些新技術(無鎖異步、等等),使得日志的吞吐量、性能比log4j 1.x提高10倍,并解決了一些死鎖的bug,而且配置更加簡單靈活,官網位址: http://logging.apache.org/log4j/2.x/manual/configuration.html
為什麼需要日志接口,直接使用具體的實作不就行了嗎?
接口用于定制規範,可以有多個實作,使用時是面向接口的(導入的包都是slf4j的包而不是具體某個日志架構中的包),即直接和接口互動,不直接使用實作,是以可以任意的更換實作而不用更改代碼中的日志相關代碼。
比如:slf4j定義了一套日志接口,項目中使用的日志架構是logback,開發中調用的所有接口都是slf4j的,不直接使用logback,調用是 自己的工程調用slf4j的接口,slf4j的接口去調用logback的實作,可以看到整個過程應用程式并沒有直接使用logback,當項目需要更換更加優秀的日志架構時(如log4j2)隻需要引入Log4j2的jar和Log4j2對應的配置檔案即可,完全不用更改Java代碼中的日志相關的代碼logger.info(“xxx”),也不用修改日志相關的類的導入的包(import org.slf4j.Logger; 
import org.slf4j.LoggerFactory;)

使用日志接口便于更換為其他日志架構,擴充卡作用
log4j、logback、log4j2都是一種日志具體實作架構,是以既可以單獨使用也可以結合slf4j一起搭配使用)
      

• 到此我們使用的是Log4j2日志架構,接下來是配置log4j,具體配置詳解如下:

<?xml version="1.0" encoding="UTF-8"?>
<!--日志級别以及優先級排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
 <!--Configuration後面的status,這個用于設定log4j2自身内部的資訊輸出,可以不設定,當設定成trace時,你會看到log4j2内部各種詳細輸出-->
<!--monitorInterval:Log4j能夠自動檢測修改配置 檔案和重新配置本身,設定間隔秒數-->
<Configuration status="WARN">

    <!--定義一些屬性-->
    <Properties>
        <Property name="PID">????</Property>
        <Property name="LOG_PATTERN">
            [%d{yyyy-MM-dd HH:mm:ss.SSS}] - ${sys:PID} --- %c{1}: %m%n
        </Property>
    </Properties>

    <!--輸出源,用于定義日志輸出的地方-->
    <Appenders>

        <!--輸出到控制台-->
        <Console name="Console" target="SYSTEM_OUT" follow="true">
            <PatternLayout
                    pattern="${LOG_PATTERN}">
            </PatternLayout>
        </Console>

        <!--檔案會列印出所有資訊,這個log每次運作程式會自動清空,由append屬性決定,适合臨時測試用-->
        <!--append為TRUE表示消息增加到指定檔案中,false表示消息覆寫指定的檔案内容,預設值是true-->
        <!--<File name="File" fileName="logs/log.log" append="false">-->
            <!--<PatternLayout>-->
                <!--<pattern>[%-5p] %d %c - %m%n</pattern>-->
            <!--</PatternLayout>-->
        <!--</File>-->

        <!--這個會列印出所有的資訊,每次大小超過size,則這size大小的日志會自動存入按年份-月份建立的檔案夾下面并進行壓縮,作為存檔 -->
        <RollingFile name="RollingAllFile" fileName="logs/all/all.log"
                     filePattern="logs/all/$${date:yyyy-MM}/all-%d{yyyy-MM-dd}-%i.log.gz">
            <PatternLayout
                    pattern="${LOG_PATTERN}" />
            <Policies>
                <!--以下兩個屬性結合filePattern使用,完成周期性的log檔案封存工作-->
                <!--TimeBasedTriggeringPolicy 基于時間的觸發政策,以下是它的兩個參數:
                    1.interval,integer型,指定兩次封存動作之間的時間間隔。機關:以日志的命名精度來确定機關,比如yyyy-MM-dd-HH 機關為小時,yyyy-MM-dd-HH-mm 機關為分鐘
                    2.modulate,boolean型,說明是否對封存時間進行調制。若modulate=true,則封存時間将以0點為邊界進行偏移計算。比如,modulate=true,interval=4hours,那麼假設上次封存日志的時間為03:00,則下次封存日志的時間為04:00,之後的封存時間依次為08:00,12:00,16:00-->
                <!--<TimeBasedTriggeringPolicy/>-->
                <!--SizeBasedTriggeringPolicy 基于日志檔案大小的觸發政策,以下配置解釋為:
                    當單個檔案達到20M後,會自動将以前的内容,先建立類似 2014-09(年-月)的目錄,然後按 "xxx-年-月-日-序号"命名,打成壓縮包-->
                <SizeBasedTriggeringPolicy size="200 MB"/>
            </Policies>
        </RollingFile>

        <!-- 添加過濾器ThresholdFilter,可以有選擇的輸出某個級别及以上的類别  onMatch="ACCEPT" onMismatch="DENY"意思是比對就接受,否則直接拒絕 -->
        <RollingFile name="RollingErrorFile" fileName="logs/error/error.log"
                     filePattern="logs/error/$${date:yyyy-MM}/%d{yyyy-MM-dd}-%i.log.gz">
            <ThresholdFilter level="ERROR"/>
            <PatternLayout
                    pattern="${LOG_PATTERN}" />
            <Policies>
                <!--<TimeBasedTriggeringPolicy/>-->
                <SizeBasedTriggeringPolicy size="200 MB"/>
            </Policies>
        </RollingFile>

        <RollingFile name="RollingWarnFile" fileName="logs/warn/warn.log"
                     filePattern="logs/warn/$${date:yyyy-MM}/%d{yyyy-MM-dd}-%i.log.gz">
            <Filters>
                <ThresholdFilter level="WARN"/>
                <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
            </Filters>
            <PatternLayout
                    pattern="${LOG_PATTERN}" />
            <Policies>
                <!--<TimeBasedTriggeringPolicy/>-->
                <SizeBasedTriggeringPolicy size="200 MB"/>
            </Policies>
        </RollingFile>

    </Appenders>

    <!--然後定義Loggers,隻有定義了Logger并引入的Appender,Appender才會生效-->
    <Loggers>
        <Logger name="org.hibernate.validator.internal.util.Version" level="WARN"/>
        <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN"/>
        <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN"/>
        <Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR"/>
        <Logger name="org.springframework" level="INFO" />
        <Logger name="com.springboot.mybatis.demo" level="DEBUG"/>
        <!--以上的logger會繼承Root,也就是說他們預設會輸出到Root下定義的符合條件的Appender中,若不想讓它繼承可以設定 additivity="false"
        并可以在Logger中設定 <AppenderRef ref="Console"/> 指定輸出到Console-->
        <Root level="INFO">
            <AppenderRef ref="Console" />
            <AppenderRef ref="RollingAllFile"/>
            <AppenderRef ref="RollingErrorFile"/>
            <AppenderRef ref="RollingWarnFile"/>
        </Root>
    </Loggers>
</Configuration>      

到此我們就算是把日志內建進去了,可以在終端看到各種log,very exciting!!! 

4.內建MybatisProvider

• Why ?

    有了它我們可以通過注解的方式結合動态SQL實作基本的增删改查操作,而不需要再在xml中寫那麼多重複繁瑣的SQL了

• Come on ↓

  First: 定義一個Mapper接口并實作基本操作,如下:

package com.springboot.mybatis.demo.mapper.common;

import com.springboot.mybatis.demo.mapper.common.provider.AutoSqlProvider;
import com.springboot.mybatis.demo.mapper.common.provider.MethodProvider;
import com.springboot.mybatis.demo.model.common.BaseModel;
import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.UpdateProvider;

import java.io.Serializable;
import java.util.List;

public interface BaseMapper<T extends BaseModel, Id extends Serializable> {

    @InsertProvider(type = AutoSqlProvider.class, method = MethodProvider.SAVE)
    int save(T entity);

    @DeleteProvider(type = AutoSqlProvider.class, method = MethodProvider.DELETE_BY_ID)
    int deleteById(Id id);

    @UpdateProvider(type = AutoSqlProvider.class, method = MethodProvider.UPDATE_BY_ID)
    int updateById(Id id);

    @SelectProvider(type = AutoSqlProvider.class, method = MethodProvider.FIND_ALL)
    List<T> findAll(T entity);

    @SelectProvider(type = AutoSqlProvider.class, method = MethodProvider.FIND_BY_ID)
    T findById(T entity);

    @SelectProvider(type = AutoSqlProvider.class, method = MethodProvider.FIND_AUTO_BY_PAGE)
    List<T> findAutoByPage(T entity);
}      

其中AutoSqlProvider是提供sql的類,MethodProvider是定義好我們使用MybatisProvider需要實作的基本持久層方法,這兩個方法具體實作如下:

package com.springboot.mybatis.demo.mapper.common.provider;

import com.google.common.base.CaseFormat;
import com.springboot.mybatis.demo.mapper.common.provider.model.MybatisTable;
import com.springboot.mybatis.demo.mapper.common.provider.utils.ProviderUtils;
import org.apache.ibatis.jdbc.SQL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.List;

public class AutoSqlProvider {
    private static Logger logger = LoggerFactory.getLogger(AutoSqlProvider.class);

    public String findAll(Object obj) {
        MybatisTable mybatisTable = ProviderUtils.getMybatisTable(obj);
        List<Field> fields = mybatisTable.getMybatisColumnList();
        SQL sql = new SQL();
        fields.forEach(field -> sql.SELECT(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName())));
        sql.FROM(mybatisTable.getName());
        logger.info(sql.toString());
        return sql.toString();
    }

    public String save(Object obj) {
     ...
        return null;
    }

    public String deleteById(String id) {
     ...
        return null;
    }

    public String findById(Object obj) {
        ...
        return null;
    }

    public String updateById(Object obj) {
        ...
        return null;
    }

    public String findAutoByPage(Object obj) {
        return null;
    }

}      
package com.springboot.mybatis.demo.mapper.common.provider;

public class MethodProvider {
    public static final String SAVE = "save";
    public static final String DELETE_BY_ID = "deleteById";
    public static final String UPDATE_BY_ID = "updateById";
    public static final String FIND_ALL = "findAll";
    public static final String FIND_BY_ID = "findById";
    public static final String FIND_AUTO_BY_PAGE = "findAutoByPage";
}      

注意:

1.如果你在BaseMapper中定義了某個方法一定要在SqlProvider類中去實作該方法,否則将報找不到該方法的錯誤

2.在動态拼接SQL的時候遇到一個問題:即使開啟了駝峰命名轉換,在拼接的時候依然需要手動将表屬性轉換,否則不會自動轉換

3.在SqlProvider中的SQL log可以去除,因為在內建日志的時候已經配置好了

4.ProviderUtils是通過反射的方式拿到表的一些基本屬性:表名,表屬性

•  到這裡MybatisProvider的基礎配置已經準備好,接下去就是讓每一個mapper接口去繼承我們這個基礎Mapper,這樣所有的基礎增删改查都由BaseMapper負責,如下:

package com.springboot.mybatis.demo.mapper;

import com.springboot.mybatis.demo.mapper.common.BaseMapper;
import com.springboot.mybatis.demo.model.User;

import java.util.List;

public interface UserMapper extends BaseMapper<User,String> {

}      

這樣UserMapper就不需要再關注那些基礎的操作了,wonderful !!!

5. 整合JSP過程

• 引入核心包

compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.0.0.RELEASE'
// 注意此處一定要是compile或者預設,不能使用providedRuntime否則jsp無法渲染
compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '9.0.6' 

      
providedRuntime group: 'org.springframework.boot', name: 'spring-boot-starter-tomcat', version: '2.0.2.RELEASE' // 此行代碼是用于解決内置tomcat和外部tomcat沖突問題,若僅使用内置tomcat則無需此行代碼      

這是兩個基本的包,其中spring-boot-starter-web會引入tomcat也就是我們常說的SpringBoot内置的tomcat,而tomcat-embed-jasper是解析jsp的包,如果這個包沒有引入或是有問題則無法渲染jsp頁面

• 修改Application啟動類

@EnableTransactionManagement 
@SpringBootApplication 
public class Application extends SpringBootServletInitializer { 
    
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        setRegisterErrorPageFilter(false);
        return application.sources(Application.class);
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}      

注意:啟動類必須繼承SpringBootServletInitializer 類并重寫configure方法

• 建立jsp頁面(目錄詳情如下)

springboot + mybatis + gradle項目建構過程

• 接下來就是配置如何去擷取jsp頁面了,有兩中選擇

一:通過在application.properties檔案中配置

spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp      

然後建立controller(注意:在Spring 2.0之後如果要傳回jsp頁面必須使用@Controller而不能使用@RestController)

@Controller // spring 2.0 如果要傳回jsp頁面必須使用Controller而不能使用RestController
public class IndexController {

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

二:通過配置檔案實作,這樣的話直接請求 http:localhost:8080/就能直接擷取到index.jsp頁面,省去了controller代碼的書寫

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {


// /static (or /public or /resources or /META-INF/resources
    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        return resolver;
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
    }

  // 此方法如果不重寫的話将無法找到index.jsp資源
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}      

6.內建Shiro認證和授權以及Session

• shiro核心

認證、授權、會話管理、緩存、加密

• 內建認證過程

(1)引包(注:包是按需引用的,以下隻是個人建構時候引用的,僅供參考↓)

// shiro
    compile group: 'org.apache.shiro', name: 'shiro-core', version: '1.3.2' // 必引包,shiro核心包
    compile group: 'org.apache.shiro', name: 'shiro-web', version: '1.3.2' // 與web整合的包
    compile group: 'org.apache.shiro', name: 'shiro-spring', version: '1.3.2' // 與spring整合的包
    compile group: 'org.apache.shiro', name: 'shiro-ehcache', version: '1.3.2' // shiro緩存      

(2)shiro配置檔案

@Configuration
public class ShiroConfig {
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //攔截器Map
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        //配置不會被攔截的路徑
        filterChainDefinitionMap.put("/static/**", "anon");
        //配置退出
        filterChainDefinitionMap.put("/logout", "logout");
     //配置需要認證才能通路的路徑
        filterChainDefinitionMap.put("/**", "authc");
     //配置需要認證和admin角色才能通路的路徑
     filterChainDefinitionMap.put("user/**","authc,roles[admin]") //注意roles中的角色可以為多個且時and的關系,即要擁有所有角色才能通路,如果要or關系可自行寫filter

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        //配置登陸路徑
        shiroFilterFactoryBean.setLoginUrl("/login");
        //配置登陸成功後跳轉的路徑
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //登陸失敗跳回登陸界面
        shiroFilterFactoryBean.setUnauthorizedUrl("/login");
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        return shiroFilterFactoryBean;
    }

    @Bean
    public ShiroRealmOne shiroRealmOne() {
        ShiroRealmOne realm = new ShiroRealmOne(); // 此處是自定義shiro規則
        return realm;
    }

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(shiroRealmOne());
     securityManager.setCacheManager(ehCacheManager());
     securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    @Bean(name = "ehCacheManager") // 将使用者資訊緩存起來
    public EhCacheManager ehCacheManager() {
return new EhCacheManager();
    }

      
  @Bean(name = "shiroCachingSessionDAO") // shiroSession
  public SessionDAO shiroCachingSessionDAO() {
      EnterpriseCacheSessionDAO sessionDao = new EnterpriseCacheSessionDAO();
      sessionDao.setSessionIdGenerator(new JavaUuidSessionIdGenerator()); // SessionId生成器
      sessionDao.setCacheManager(ehCacheManager()); // 緩存
      return sessionDao;
  }      
  @Bean(name = "sessionManager")
  public DefaultWebSessionManager sessionManager() {
      DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
      defaultWebSessionManager.setGlobalSessionTimeout(1000 * 60);
      defaultWebSessionManager.setSessionDAO(shiroCachingSessionDAO());
      return defaultWebSessionManager;
  }
      
}      

自定義realm,繼承了AuthorizationInfo實作簡單的登陸驗證

package com.springboot.mybatis.demo.config.realm;

import com.springboot.mybatis.demo.model.Permission;
import com.springboot.mybatis.demo.model.Role;
import com.springboot.mybatis.demo.model.User;
import com.springboot.mybatis.demo.service.PermissionService;
import com.springboot.mybatis.demo.service.RoleService;
import com.springboot.mybatis.demo.service.UserService;
import com.springboot.mybatis.demo.service.impl.PermissionServiceImpl;
import com.springboot.mybatis.demo.service.impl.RoleServiceImpl;
import com.springboot.mybatis.demo.service.impl.UserServiceImpl;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class ShiroRealmOne extends AuthorizingRealm {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private UserService userServiceImpl;

    @Autowired
    private RoleService roleServiceImpl;

    @Autowired
    private PermissionService permissionServiceImpl;

    //授權(這裡對授權不做講解,可忽略)
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        logger.info("doGetAuthorizationInfo+" + principalCollection.toString());
        User user = userServiceImpl.findByUserName((String) principalCollection.getPrimaryPrincipal());
        List<Role> roleList = roleServiceImpl.findByUserId(user.getId());
        List<Permission> permissionList = roleList != null && !roleList.isEmpty() ? permissionServiceImpl.findByRoleIds(roleList.stream().map(Role::getId).collect(Collectors.toList())) : new ArrayList<>();
        SecurityUtils.getSubject().getSession().setAttribute(String.valueOf(user.getId()), SecurityUtils.getSubject().getPrincipals());
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //賦予角色
        for (Role role : roleList) {
            simpleAuthorizationInfo.addRole(role.getRolName());
        }
        //賦予權限
        for (Permission permission : permissionList) {
            simpleAuthorizationInfo.addStringPermission(permission.getPrmName());
        }
        return simpleAuthorizationInfo;
    }
  // 認證
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        logger.info("doGetAuthenticationInfo +" + authenticationToken.toString());

        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String userName = token.getUsername();
        logger.info(userName + token.getPassword());

        User user = userServiceImpl.findByUserName(token.getUsername());
        if (user != null) {
            Session session = SecurityUtils.getSubject().getSession();
            session.setAttribute("user", user);
            return new SimpleAuthenticationInfo(userName, user.getUsrPassword(), getName());
        } else {
            return null;
        }
    }
}      

到此shrio認證簡單配置就配置好了,接下來就是驗證了

控制器

package com.springboot.mybatis.demo.controller;


import com.springboot.mybatis.demo.common.utils.SelfStringUtils;
import com.springboot.mybatis.demo.controller.common.BaseController;
import com.springboot.mybatis.demo.model.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
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;


@Controller
public class IndexController extends BaseController{

    @PostMapping("login")
    public String login(User user, Model model) {
        if (user == null || SelfStringUtils.isEmpty(user.getUsrName()) || SelfStringUtils.isEmpty(user.getUsrPassword()) ) {
            model.addAttribute("warn","請填寫完整使用者名和密碼!");
            return "login";
        }
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsrName(), user.getUsrPassword());
        token.setRememberMe(true);
        try {
            subject.login(token);
        } catch (AuthenticationException e) {
            model.addAttribute("error","使用者名或密碼錯誤,請重新登陸!");
            return "login";
        }
        return "index";
    }

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

}      

login jsp:

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2018/7/29
  Time: 14:34
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登陸</title>
</head>
<body>
    <form action="login" method="POST">
        User Name: <input type="text" name="usrName">
        <br />
        User Password: <input type="text" name="usrPassword" />
        <input type="submit" value="Submit" />
    </form>
    <span style="color: #b3b20a;">${warn}</span>
    <span style="color:#b3130f;">${error}</span>
</body>
</html>      

index jsp:

<%--
  Created by IntelliJ IDEA.
  User: pc
  Date: 2018/7/23
  Time: 14:02
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>Welcome to here!</h1>
</body>
</html>      

正常情況分析:

1.未登入時通路非login接口直接跳回login頁面

2.登陸失敗傳回賬戶或密碼錯誤

3.未填寫完整賬戶和密碼傳回請填寫完整賬戶和密碼

4.登陸成功跳轉到index頁面,如果不是admin角色則不能通路user/**的路徑,其他可以正常通路

未完!待續。。。如有不妥之處,請提建議和意見,謝謝