1.從 Spring boot官網
根據需求下載下傳腳手架或者到GitHub上去搜尋對應的腳手架項目,D_iao ^0^
• 檔案目錄如下(此處generatorConfig.xml 和 log4j2.xml檔案請忽略,後續會講解)
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是類所處的包路徑(確定包是存在的,否則無法生成),也就相當于
• 代碼生成
配置完成之後首先得在資料庫中建立對應的表,然後確定資料庫能正常通路,最後在終端執行gradle mbGenerator或者點選如下任務
成功之後它會生成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頁面(目錄詳情如下)
• 接下來就是配置如何去擷取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/**的路徑,其他可以正常通路
未完!待續。。。如有不妥之處,請提建議和意見,謝謝