生产可用的SpringBoot框架整合

技术说明:

  • JDK1.8+
  • 基础框架 springboot + springMvc
  • 持久化框架 mybatis
  • 缓存 redis
  • 数据库 Mysql
  • Maven jar管理
  • mybatis-generator 自动化代码生成
  • Assembly 打包工具 + 启动脚本
  • Logback 日志管理
  • 数据库连接池druid
  • 分页pagehelper

待引入

  • Swagger2 日志文档生成
  • spring security 权限管理
  • 事物加注解即可@Transactional
  • 数据库加密
  • 统一异常管理MyException
  • 等等

构建项目

  • 地址:http://start.spring.io/
  • Jdk版本1.8及以上

image.png

  • 如你所见,项目里面基本没有代码,除了几个空目录外,还包含如下几样东西。
    pom.xml:Maven构建说明文件。
    DemoApplication.java:一个带有main()方法的类,用于启动应用程序(关键)。
    DemoApplicationTests.java:一个空的Junit测试类,它加载了一个使用Spring Boot字典配置功能的Spring应用程序上下文。
    application.properties:一个空的properties文件,你可以根据需要添加配置属性。

初始化代码

创建TestController类,添加如下代码。

@RestController
@EnableAutoConfiguration
public class TestController {

    @RequestMapping("/")
    public String test() {
        return "Hello world";
    }
}

启动程序

在DemoApplication.java 文件点击右键运行main方法(也可以另外一种启动方式:mvn spring-boot:run -Dspring.profiles.active=dev),之后运行http://localhost:8080/就会看到 Hello world了。

  • 注意一点:DemoApplication这个启动类必须放在最外层,要不会抱一个错误Whitelabel Error Page,详细解释也可以看官网http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#using-boot-structuring-your-code

代码规范

接下来我们做一下代码规范要求,以后都按照这种格式统一书写。

业务分层

  • domain层:实体类。
  • api层:对外接口。
  • service层:业务层。
  • dao层:持久层。
  • Controller层:控制层。

pom文件配置

以下是基础jar文件:

 <dependencies>
    <!-- base -->
    <dependency>
        <groupId>com.lulj</groupId>
        <artifactId>bruce-bean</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </dependency>

    <!-- spring boot start -->

    <!--核心模块,包括自动配置支持、日志和YAM-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <!--测试模块,包括JUnit、Hamcrest、Mockito-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!--支持web的模块-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--引用AOP-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

    <!--启动时启动内置tomcat -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
    </dependency>

    <!-- 集成log-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
    </dependency>


    <!-- MyBatis -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.3.1</version>
    </dependency>


    <!--mapper-->
    <dependency>
        <groupId>tk.mybatis</groupId>
        <artifactId>mapper-spring-boot-starter</artifactId>
        <version>1.2.4</version>
    </dependency>

    <!-- 热部署,不用重启 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>


    <!-- 添加freemarker依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-freemarker</artifactId>
    </dependency>


  <!-- spring boot end -->


    <!--spring2.0集成redis所需common-pool2-->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.5.0</version>
    </dependency>

    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.5</version>
    </dependency>

    <!-- 将作为Redis对象序列化器 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.47</version>
    </dependency>

    <dependency>
        <groupId>net.sf.json-lib</groupId>
        <artifactId>json-lib</artifactId>
        <version>2.4</version>
        <classifier>jdk15</classifier>
    </dependency>


    <!-- MySql驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <!-- 集成druid,使用连接池-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.0</version>
    </dependency>

    <!-- lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.10</version>
    </dependency>

    <!--pagehelper-->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>1.2.3</version>
    </dependency>

    <dependency>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
    </dependency>

框架配置

在resources目录下新建application.properties或者application.yml配置文件,本文使用properties,添加如下配置:

#公共配置与profiles选择无关
mybatis.typeAliasesPackage=com.example.demo.domain
mybatis.mapperLocations=classpath*:com/example/demo/dao/**/*Mapper.xml
configuration.cacheEnabled=true
configuration.useGeneratedKeys=true
configuration.defaultExecutorType=REUSE
configuration.log-imp=LOGBACK
#驼峰标识
configuration.mapUnderscoreToCamelCase=true
configuration.jdbcTypeForNull=NULL
#sql日志
logging.level.com.example.demo..dao=DEBUG
###########################################开发配置###########################################
#端口
server.port=8080
#项目名称
server.servlet.context-path=/demo
#服务名称
spring.application.name=demo

###mysql驱动配置信息
spring.datasource.url=jdbc:mysql://192.168.179.191:3306/wx?useUnicode=true&useSSL=false&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# 连接池的配置信息
spring.datasource.filters=stat
spring.datasource.maxActive=20
spring.datasource.initialSize=1
spring.datasource.maxWait=60000
spring.datasource.minIdle=1
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=select 'x'
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
poolPreparedStatements=true
spring.datasource.maxOpenPreparedStatements=20

########分页#######
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql
pagehelper.offset-as-page-num=true
pagehelper.row-bounds-with-count=true

引入日志配置

SpringBoot 的日志文件放在resources目录下,在resources目录下新建logback.xml.

logback.xml配置及说明

   <?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2010-2011 The myBatis Team Licensed under the Apache License,
    Version 2.0 (the "License"); you may not use this file except in compliance
    with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
    Unless required by applicable law or agreed to in writing, software distributed
    under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
    OR CONDITIONS OF ANY KIND, either express or implied. See the License for
    the specific language governing permissions and limitations under the License. -->
<configuration>
    <contextName>demo</contextName>

    <property name="log.name" value="demo" />
    <property name="log.dir" value="../logs" />

    <appender name="FILEERROR"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.dir}/log_error.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.dir}/error/${log.name}-error-%d{yyyy-MM-dd}.%i.log
            </fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>2MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <append>true</append>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>[%date{ISO8601}] [%-5level] - [%thread] [%X{requestId}] [%logger] [%X{akkaSource}] - %msg
                %rootException %n
            </pattern>
            <charset>utf-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>error</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <appender name="FILEWARN"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.dir}/${log.name}_warn.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.dir}/warn/${log.name}-warn-%d{yyyy-MM-dd}.%i.log
            </fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>2MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <append>true</append>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>[%date{ISO8601}] [%-5level] - [%thread] [%X{requestId}] [%logger] [%X{akkaSource}] - %msg
                %rootException %n
            </pattern>
            <charset>utf-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>warn</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <appender name="FILEINFO"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.dir}/${log.name}_info.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.dir}/info/${log.name}-info-%d{yyyy-MM-dd}.%i.log
            </fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>2MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <append>true</append>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>[%date{ISO8601}] [%-5level] - [%thread] [%X{requestId}] [%logger] [%X{akkaSource}] - %msg
                %rootException %n
            </pattern>
            <charset>utf-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>info</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>
                %black() %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight([%-5level]) %cyan([%X{requestId}]) %boldMagenta([%logger]) [%X{akkaSource}] - %cyan(%msg
                %rootException %n)
            </pattern>
            <charset>utf-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
    </appender>

    <logger name="org.springframework" level="WARN" />
    <logger name="org.hibernate" level="WARN" />

    <root level="INFO">
        <appender-ref ref="FILEERROR" />
        <appender-ref ref="FILEWARN" />
        <appender-ref ref="FILEINFO" />

        <!-- 生产环境将请stdout,testfile去掉 -->
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

日志切面

@Aspect
@Order(-99)
@Configuration
@Slf4j
public class LogConfig {

    @Pointcut("execution(* com.example.demo.controller..*.*(..))," +
            "execution(* com.example.demo.api..*.*(..))")
    public void executionService() {

    }

    @Before(value = "executionService()")
    public void doBefore(JoinPoint joinPoint) {
        String requestId = String.valueOf(UUID.randomUUID());
        MDC.put("requestId", requestId);

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        log.info("HTTP URL : " + request.getRequestURL().toString());
        log.info("HTTP METHOD : " + request.getMethod());
        log.info("IP : " + IPAddrUtil.localAddress());
        log.info("CLASS METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "."
                + joinPoint.getSignature().getName());
        log.info("PARAMS : " + Arrays.toString(joinPoint.getArgs()));
    }

    @AfterReturning(pointcut = "executionService()", returning = "returnValue")
    public void doAfterReturning(Object returnValue) {
        log.info("=====>@AfterReturning:The response parameter is:{}", returnValue);
        MDC.clear();
    }

    @Around("executionService()")
    public Object timeAround(ProceedingJoinPoint joinPoint) {
        long startTime = System.currentTimeMillis();
        Object obj = null;
        try {
            obj = joinPoint.proceed();
        } catch (Throwable e) {
            log.error("=====>Counts the elapsed time of a method execution to surround notification errors:", e);
        }
        long endTime = System.currentTimeMillis();
        log.info("=====>Processing this request takes time:{} ms", endTime - startTime);
        return obj;
    }
}

shiro引入

首先pom文件要引入shiro的jar包,如下:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>

引入模板引擎freemarker:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

<dependency>
    <groupId>net.mingsoft</groupId>
    <artifactId>shiro-freemarker-tags</artifactId>
    <version>0.1</version>
</dependency>

配置ShiroConfig

package com.example.demo.config;

import com.example.demo.core.shiro.MyShiroRealm;
import com.example.demo.domain.properties.RedisProperties;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author lu
 */
@ConditionalOnWebApplication
@Configuration
@EnableConfigurationProperties({RedisProperties.class})
public class ShiroConfig {

    @Bean
    public FilterRegistrationBean delegatingFilterProxy() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
        proxy.setTargetFilterLifecycle(true);
        proxy.setTargetBeanName("shiroFilter");
        filterRegistrationBean.setFilter(proxy);
        return filterRegistrationBean;
    }

    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/login/loginPage");
        shiroFilterFactoryBean.setSuccessUrl("/login/main");
        shiroFilterFactoryBean.setUnauthorizedUrl("/login/500");
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/login/**", "anon");
        filterChainDefinitionMap.put("/res/** ", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        //错误页面,认证不通过跳转
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }


    //    @Bean
//    public Realm shiroRealm(HashedCredentialsMatcher matcher) {
//        MyShiroRealm myShiroRealm = new MyShiroRealm();
//        myShiroRealm.setCredentialsMatcher(matcher);
//        return myShiroRealm;
//    }
    @Bean
    public Realm shiroRealm() {
        return new MyShiroRealm();
    }


    @Bean
    public SecurityManager securityManager(RedisProperties commonProperties) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRememberMeManager(rememberMeManager());
        securityManager.setSessionManager(sessionManager(commonProperties));
        securityManager.setCacheManager(cacheManager(commonProperties));
        securityManager.setRealm(shiroRealm());
        return securityManager;
    }
//    @Bean
//    public SecurityManager securityManager(RedisProperties commonProperties) {
//        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//        securityManager.setRememberMeManager(rememberMeManager());
//        securityManager.setSessionManager(sessionManager(commonProperties));
//        securityManager.setCacheManager(cacheManager(commonProperties));
//        securityManager.setRealm(shiroRealm(hashedCredentialsMatcher()));
//        return securityManager;
//    }

    @Bean
    public SimpleCookie rememberCookie() {
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        return simpleCookie;
    }

    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberCookie());
        cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return cookieRememberMeManager;
    }

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

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 开启Shiro的注解支持
     * 比如:@RequireRoles @RequireUsers
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public SessionManager sessionManager(RedisProperties commonProperties) {
        MySessionManager mySessionManager = new MySessionManager();
        mySessionManager.setSessionDAO(redisSessionDAO(commonProperties));
//        mySessionManager.setCacheManager(cacheManager());
        mySessionManager.setSessionIdUrlRewritingEnabled(true);
        return mySessionManager;
    }

    @Bean
    public RedisManager redisManager(RedisProperties commonProperties) {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(commonProperties.getHost());
        redisManager.setPort(commonProperties.getPort());
        redisManager.setTimeout(commonProperties.getTimeout());
        redisManager.setPassword(commonProperties.getPassword());
        return redisManager;
    }

    /**
     * redis实现缓存
     *
     * @return
     */
    @Bean
    public RedisCacheManager cacheManager(RedisProperties commonProperties) {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager(commonProperties));
        return redisCacheManager;
    }

    /**
     * 使用Redis实现 shiro sessionDao
     *
     * @return
     */
    @Bean
    public RedisSessionDAO redisSessionDAO(RedisProperties commonProperties) {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager(commonProperties));
        return redisSessionDAO;
    }


}

MySessionManager管理

package com.example.demo.config;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;

public class MySessionManager extends DefaultWebSessionManager {

    private static final String AUTHORIZATION = "X-Token";
    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    public MySessionManager() {
    }

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        //获取请求头中X-Token中保存的sessionId
        String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
        if (!StringUtils.isEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        } else {
            //否则默认从cookie中获取sessionId
            return super.getSessionId(request, response);
        }
    }
}

reids缓存

package com.example.demo.domain.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "spring.redis")
@Data
public class RedisProperties {
    private String host;
    private int port;
    private String password;
    private int timeout;
    private int database;

}

你的ShiroRealm 处理

package com.example.demo.core.shiro;

import com.example.demo.domain.User;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * shiroRealm 重写权限过滤
 *
 * @author lu
 */
@Component
public class MyShiroRealm extends AuthorizingRealm {


    /**
     * 登录信息和用户验证信息验证(non-Javadoc)
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        String username = (String) token.getPrincipal(); // 得到用户名
        String password = new String((char[]) token.getCredentials()); // 得到密码

        if (null != username && null != password) {
            return new SimpleAuthenticationInfo(username, password, getName());
        } else {
            return null;
        }

    }

    /**
     * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法(non-Javadoc)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
        User user = null;
        try {
            user = AuthUtil.getCurrentUser();
        } catch (Exception e) {
            e.printStackTrace();
        }
        SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();
        simpleAuthorInfo.addStringPermissions(getPermCodes(user));
        return simpleAuthorInfo;

    }

    /**
     * 获取权限,string存放的是权限编码
     *
     * @param user
     * @return
     */
    private List<String> getPermCodes(User user) {

        //TODO 你的权限编码处理
        return null;
    }

}

用户session

package com.example.demo.core.shiro;

import com.example.demo.domain.User;
import com.lulj.base.json.FastjsonUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;

/**
 * 用户公共类
 *
 * @author lu
 *
 */
public class AuthUtil {

    /**
     * 获取当前用户
     *
     * @return
     */
    public static User getCurrentUser() throws Exception {
        User user;
        Subject sub = SecurityUtils.getSubject();
        Session session = sub.getSession();
        Object userJson = session.getAttribute("session_user");
        if (userJson != null) {
            user = FastjsonUtils.jsonToBean(User.class, userJson.toString());
        } else {
            return null;
        }
        return user;
    }

}

Shiro 权限控制

主要的部分是在shiroFilter进行过滤
image.png

MyShiroRealm做权限编码校验
image.png

引入自动化代码生成工具

pom引入

<!--添加mybatis generator maven插件-->
<plugin>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-maven-plugin</artifactId>
    <version>1.3.5</version>
    <executions>
        <execution>
            <id>Generate MyBatis Artifacts</id>
            <!--<phase>package</phase>-->
            <phase>deploy</phase>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
    <dependencies>
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.5</version>
        </dependency>
        <!--此处必须添加mysql oracle驱动包-->
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>12.1.0.2.0</version>
            <scope>system</scope>
            <systemPath>${basedir}/lib/ojdbc6.jar</systemPath>
        </dependency>
    </dependencies>
    <configuration>
        <!--generatorConfig.xml位置-->
        <configurationFile>src/main/resources/mybatis-generator/generatorConfigOracle.xml
        </configurationFile>
        <verbose>true</verbose>
        <overwrite>true</overwrite>
    </configuration>
</plugin>

引入mybatis-generator

在resources下新建mybatis-generator并引入generatorConfigOracle.xml
mybatisGeneratorinit.properties

###generatorConfigOracle.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>
    <!--数据库驱动 -->
    <properties resource="mybatis-generator/mybatisGeneratorinit.properties"/>


    <!-- 用户相关 -->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!-- 是否去除自动生成的注释 true:是 : false:否 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!-- 数据库连接-->
        <jdbcConnection driverClass="${jdbc.driverClassName}"
                        connectionURL="${jdbc.url}"
                        userId="${jdbc.username}"
                        password="${jdbc.password}">
        </jdbcConnection>

        <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
           NUMERIC 类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>

        <!--指定javaBean生成的位置 javaBean生成的位置-->
        <javaModelGenerator targetPackage="${lulj.com}.domain.${lulj.domainObjectName}" targetProject="src/main/java">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="true"/>
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

        <!--sql映射文件生成的位置-->
        <sqlMapGenerator targetPackage="${lulj.com}.dao.${lulj.domainObjectName}" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>

        <!--指定dao接口生成的位置-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="${lulj.com}.dao.${lulj.domainObjectName}"
                             targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <!--table是指定每个表的生成策略 生成对应表及类名-->
        <table tableName="${lulj.tableName}" domainObjectName="${lulj.domainObjectName}"
               enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"
               enableSelectByExample="false" selectByExampleQueryId="false">
<!--            <property name="useActualColumnNames" value="true"/>-->

        </table>

    </context>
</generatorConfiguration>

mybatisGeneratorinit.properties根据自己的需求进行配置

jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@118.24.110.49:1521:xe
jdbc.username=
jdbc.password=
#表名
lulj.tableName=
#生成实体名
lulj.domainObjectName=
#生成包路径
lulj.com=

执行

在maven中直接运行就可以了
image.png

引入assembly

在src下新建assembly.xml内容如下:

<assembly>
    <id>bin</id>
    <formats>
        <!--支持 zip,tar,tar.gz,tar.bz2,jar,dir,war 等 -->
        <format>tar.gz</format>
        <format>zip</format>
        <format>dir</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <fileSets>
        <fileSet>
            <directory>src/assembly/conf</directory>
            <outputDirectory>conf</outputDirectory>
            <fileMode>0644</fileMode>
        </fileSet>
        <fileSet>
            <directory>${profile.dir}</directory>
            <outputDirectory>conf</outputDirectory>
            <!-- 表示的是包含下面格式的资源文件 -->
            <includes>
                <include>*.xml</include>
                <include>*.properties</include>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
            </includes>
            <fileMode>0644</fileMode>
        </fileSet>
        <fileSet>
            <directory>src/assembly/bin</directory>
            <outputDirectory>bin</outputDirectory>
            <fileMode>0755</fileMode>
        </fileSet>
        <fileSet>
            <directory>src/assembly/logs</directory>
            <outputDirectory>logs</outputDirectory>
            <fileMode>0644</fileMode>
        </fileSet>


        <fileSet>
            <directory>${project.build.directory}</directory>
            <outputDirectory>lib</outputDirectory>
            <includes>
                <include>*.jar</include>
            </includes>
        </fileSet>
    </fileSets>
</assembly>

增加脚本配置

脚本参考:https://www.jianshu.com/p/719133b1bad5

示例
image.png

pom引入

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <configuration>
        <descriptors>
            <descriptor>src/assembly/assembly.xml</descriptor>
        </descriptors>
    </configuration>
    <executions>
        <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>


<resource>
    <directory>src/main/resources</directory>
    <includes>
        <include>**/*.*</include>
    </includes>
    <excludes>
        <exclude>**/*.ttf</exclude>
        <exclude>**/*.woff</exclude>
    </excludes>
    <filtering>true</filtering>
</resource>
<resource>
    <directory>src/main/resources</directory>
    <filtering>false</filtering>
    <includes>
        <include>**/*.ttf</include>
        <include>**/*.woff</include>
    </includes>
</resource>

结果

重新maven打包,结果我们发现如下结构:
image.png

解压zip包会得到如下目录
image.png

说明

  • 本文只做学习参考,如有任何不准确的地方欢迎指正。
  • 源码参考 :https://gitee.com/lulongji/springboot-demo.git

猜你喜欢

转载自blog.csdn.net/lulongji2035/article/details/107982836
今日推荐