springboot + mybatis + gradle项目构建过程

  1. 云栖社区>
  2. 博客>
  3. 正文

springboot + mybatis + gradle项目构建过程

liuzhijian 2018-07-07 13:08:00 浏览1120
展开阅读全文

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/**的路径,其他可以正常访问

 

 

未完!待续。。。如有不妥之处,请提建议和意见,谢谢

 

 

网友评论

登录后评论
0/500
评论
liuzhijian
+ 关注