Commit 59ea2583 authored by xiaozhe.chen's avatar xiaozhe.chen

添加初始版本客服系统代码

parents
Pipeline #294 canceled with stages
<?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>
<groupId>cn.quantgroup</groupId>
<artifactId>customer-service</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>cn.quantgroup</groupId>
<artifactId>commons-parent</artifactId>
<version>0.2.4</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-context-support.version>4.1.6.RELEASE</spring-context-support.version>
<maven.test.skip>true</maven.test.skip>
<!--temporary-->
<okhttp.version>3.4.2</okhttp.version>
<retrofit.version>2.1.0</retrofit.version>
<rxjava.version>1.2.3</rxjava.version>
</properties>
<build>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<includes>
<include>
**/**
</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.31</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.1</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.4</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.4</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>${okhttp.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>${retrofit.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>adapter-rxjava</artifactId>
<version>${retrofit.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-simplexml</artifactId>
<version>${retrofit.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-gson</artifactId>
<version>${retrofit.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<dependency>
<groupId>cn.quantgroup</groupId>
<artifactId>commons-spring</artifactId>
</dependency>
<dependency>
<groupId>cn.quantgroup</groupId>
<artifactId>commons-core</artifactId>
</dependency>
<dependency>
<groupId>cn.quantgroup</groupId>
<artifactId>shutdown-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.quantgroup</groupId>
<artifactId>idgenerator-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<profiles>
<profile>
<id>dev</id>
<properties>
<project.environment>dev</project.environment>
</properties>
<build>
<resources>
<resource>
<directory>
${project.basedir}/src/main/resources/config/dev
</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</profile>
<profile>
<id>product</id>
<properties>
<project.environment>product</project.environment>
</properties>
<build>
<resources>
<resource>
<directory>
${project.basedir}/src/main/resources/config/product
</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</profile>
</profiles>
</project>
\ No newline at end of file
package cn.quantgroup.customer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
@Configuration
@EnableAspectJAutoProxy
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 24 * 60 * 60)
@SpringBootApplication(scanBasePackages = {
"cn.quantgroup.customer"
})
public class Bootstrap {
public static void main(String[] args) {
SpringApplication.run(Bootstrap.class, args);
}
}
package cn.quantgroup.customer.config.data;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;
@Configuration
@EnableJpaRepositories(basePackages = "cn.quantgroup.customer.repo")
@EnableTransactionManagement
public class DBConfig {
@Value("${data.mysql.jdbc-url}")
private String jdbcUrl;
@Value("${data.mysql.password}")
private String password;
@Value("${data.mysql.user}")
private String user;
@Value("${data.mysql.max-pool-size}")
private Integer maxPoolSize;
@Bean
@DependsOn(value = "dataSource")
public EntityManagerFactory entityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
entityManager.setDataSource(dataSource);
entityManager.setPackagesToScan("cn.quantgroup.customer");
entityManager.setPersistenceUnitName("dataSource");
Properties properties = new Properties();
properties.put("hibernate.jdbc.batch_size", 30);
properties.put("hibernate.order_inserts", true);
properties.put("hibernate.order_updates", true);
entityManager.setJpaProperties(properties);
entityManager.setJpaVendorAdapter(jpaVendorAdapter());
entityManager.afterPropertiesSet();
return entityManager.getObject();
}
@Bean
@Primary
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(jdbcUrl);
config.setPassword(password);
config.setUsername(user);
config.setMaximumPoolSize(maxPoolSize);
config.setMinimumIdle(20);
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
return new HikariDataSource(config);
}
private JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(false);
hibernateJpaVendorAdapter.setGenerateDdl(false);
hibernateJpaVendorAdapter.setDatabase(Database.MYSQL);
return hibernateJpaVendorAdapter;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
package cn.quantgroup.customer.config.data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
import java.util.HashSet;
import java.util.Set;
@Configuration
public class RedisConfig {
@Value("${data.redis.defaultExpiration}")
private Long defaultExpiration;
@Value("${redis.master.host}")
private String masterHost;
@Value("${redis.master.port}")
private int masterPort;
@Value("${redis.master.name}")
private String masterName;
@Value("${redis.sentinel1.host}")
private String sentinel1Host;
@Value("${redis.sentinel1.port}")
private int sentinel1port;
@Value("${redis.sentinel2.host}")
private String sentinel2Host;
@Value("${redis.sentinel2.port}")
private int sentinel2port;
@Value("${redis.sentinel3.host}")
private String sentinel3Host;
@Value("${redis.sentinel3.port}")
private int sentinel3port;
private RedisConnectionFactory generateDevConnectionFactory() {
JedisConnectionFactory factory = new JedisConnectionFactory();
factory.setHostName(masterHost);
factory.setPort(masterPort);
factory.setUsePool(true);
factory.setConvertPipelineAndTxResults(true);
JedisPoolConfig poolConfig = generatePoolConfig();
factory.setPoolConfig(poolConfig);
factory.afterPropertiesSet();
return factory;
}
private RedisConnectionFactory generateReleaseConnectionFactory() {
RedisSentinelConfiguration sentinelConfiguration = new RedisSentinelConfiguration();
RedisNode master = new RedisNode(masterHost, masterPort);
master.setName(masterName);
Set<RedisNode> sentinels = new HashSet<>();
RedisNode sentinel1 = new RedisNode(sentinel1Host, sentinel1port);
RedisNode sentinel2 = new RedisNode(sentinel2Host, sentinel2port);
RedisNode sentinel3 = new RedisNode(sentinel3Host, sentinel3port);
sentinels.add(sentinel1);
sentinels.add(sentinel2);
sentinels.add(sentinel3);
sentinelConfiguration.setMaster(master);
sentinelConfiguration.setSentinels(sentinels);
JedisPoolConfig poolConfig = generatePoolConfig();
JedisConnectionFactory factory = new JedisConnectionFactory(sentinelConfiguration, poolConfig);
factory.setHostName(masterHost);
factory.setPort(masterPort);
factory.setTimeout(10000);
factory.setUsePool(true);
factory.setConvertPipelineAndTxResults(true);
factory.afterPropertiesSet();
return factory;
}
private JedisPoolConfig generatePoolConfig() {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMinIdle(20);
poolConfig.setMaxTotal(300);
poolConfig.setMaxWaitMillis(5000);
poolConfig.setTestOnBorrow(true);
return poolConfig;
}
@Bean(name = "redisConnectionFactory")
RedisConnectionFactory factory() {
if (StringUtils.isEmpty(masterName)) {
return generateDevConnectionFactory();
} else {
return generateReleaseConnectionFactory();
}
}
@Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate(
RedisConnectionFactory factory) {
final RedisTemplate<String, Object> template = new RedisTemplate<>();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
template.setEnableTransactionSupport(false);
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setValueSerializer(jdkSerializationRedisSerializer);
template.setDefaultSerializer(jdkSerializationRedisSerializer);
template.setConnectionFactory(factory);
return template;
}
@Bean(name = "stringRedisTemplate")
public RedisTemplate<String, String> stringRedisTemplate(
RedisConnectionFactory factory) {
final RedisTemplate<String, String> template = new RedisTemplate<>();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setEnableTransactionSupport(false);
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setValueSerializer(stringRedisSerializer);
template.setDefaultSerializer(stringRedisSerializer);
template.setConnectionFactory(factory);
return template;
}
@Bean(name = "cacheManager")
public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
cacheManager.setDefaultExpiration(defaultExpiration);
cacheManager.setUsePrefix(true);
return cacheManager;
}
}
package cn.quantgroup.customer.config.http.mvc;
import cn.quantgroup.customer.config.http.mvc.converter.DateConverter;
import cn.quantgroup.customer.config.http.mvc.converter.IEnumConverterFactory;
import cn.quantgroup.customer.config.http.mvc.converter.LocalDateConverter;
import cn.quantgroup.customer.config.http.mvc.converter.LocalDateTimeConverter;
import com.google.common.collect.Lists;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Configuration
public class WebMvcConfigure extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
StringHttpMessageConverter stringConverter =
new StringHttpMessageConverter(StandardCharsets.UTF_8);
stringConverter.setSupportedMediaTypes(Lists.newArrayList(MediaType.TEXT_HTML, MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN));
stringConverter.setWriteAcceptCharset(true);
converters.add(stringConverter);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
}
//添加枚举参数转化器
//LocalDate,LocalDateTime转换器也在此添加
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(new IEnumConverterFactory());
registry.addConverter(new LocalDateConverter("yyyy-MM-dd"));
registry.addConverter(new LocalDateTimeConverter("yyyy-MM-dd HH:mm:ss.SSS"));
registry.addConverter(new DateConverter());
}
}
package cn.quantgroup.customer.config.http.mvc.converter;
import org.springframework.core.convert.converter.Converter;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
if (source == null || source.isEmpty()) {
return null;
}
return Date.from(Instant.from(LocalDate.parse(source).atStartOfDay().atZone(ZoneId.systemDefault())));
}
}
package cn.quantgroup.customer.config.http.mvc.converter;
import org.springframework.core.convert.converter.Converter;
public class IEnumConverter<T extends IEnumValue> implements Converter<String, T> {
private final Class<T> enumType;
IEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
@Override
public T convert(String s) {
T t;
try {
t = IEnumValue.parse(Integer.parseInt(s), enumType);
} catch (NumberFormatException e) {
t = IEnumValue.parseByName(s, enumType);
}
return t;
}
}
package cn.quantgroup.customer.config.http.mvc.converter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
public class IEnumConverterFactory implements ConverterFactory<String, IEnumValue> {
private static final Map<Class, Converter> CONVERTER_MAP = new WeakHashMap<>();
@Override
public <T extends IEnumValue> Converter<String, T> getConverter(Class<T> targetType) {
Converter result = CONVERTER_MAP.get(targetType);
if (Objects.isNull(result)) {
result = new IEnumConverter<>(targetType);
CONVERTER_MAP.put(targetType, result);
}
return result;
}
}
package cn.quantgroup.customer.config.http.mvc.converter;
public interface IEnumValue {
static <T extends IEnumValue> T parse(int code, Class<T> clazz) {
T[] all = clazz.getEnumConstants();
for (T current : all) {
if (current.getValue() == code) {
return current;
}
}
throw new IllegalArgumentException("No element matches " + code);
}
static <T extends IEnumValue> T parseByName(String name, Class<T> clazz) {
T[] all = clazz.getEnumConstants();
for (T current : all) {
if (current.toString().equals(name)) {
return current;
}
}
throw new IllegalArgumentException("No element matches " + name);
}
int getValue();
}
package cn.quantgroup.customer.config.http.mvc.converter;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.converter.Converter;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class LocalDateConverter implements Converter<String, LocalDate> {
private final DateTimeFormatter formatter;
public LocalDateConverter(String dateFormat) {
this.formatter = DateTimeFormatter.ofPattern(dateFormat);
}
@Override
public LocalDate convert(String source) {
if (source == null || source.isEmpty()) {
return null;
}
LocalDate obj;
try {
obj = LocalDate.parse(source, formatter);
} catch (ConversionFailedException e) {
throw new IllegalArgumentException("date format error!");
}
return obj;
}
}
package cn.quantgroup.customer.config.http.mvc.converter;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.converter.Converter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class LocalDateTimeConverter implements Converter<String, LocalDateTime> {
private final DateTimeFormatter formatter;
public LocalDateTimeConverter(String dateFormat) {
this.formatter = DateTimeFormatter.ofPattern(dateFormat);
}
@Override
public LocalDateTime convert(String source) {
if (source == null || source.isEmpty()) {
return null;
}
LocalDateTime obj;
try {
obj = LocalDateTime.parse(source, formatter);
} catch (ConversionFailedException e) {
throw new IllegalArgumentException("date format error!");
}
return obj;
}
}
package cn.quantgroup.customer.config.http.mvc.filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
public class CORSFilter implements Filter {
private String allowedHeaders;
@Override
public void init(FilterConfig filterConfig) {
allowedHeaders = "filter.allowedHeaders=Origin, No-Cache, x-auth-token, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type,Authorization";
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
String originHeader = ((HttpServletRequest) servletRequest).getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", originHeader);
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE");
response.setHeader("Access-Control-Allow-Headers", allowedHeaders);
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Max-Age", "3600");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
package cn.quantgroup.customer.config.http.security;
import cn.quantgroup.customer.constant.Constant;
import cn.quantgroup.customer.service.IUserService;
import cn.quantgroup.customer.util.PwdUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.util.StringUtils;
import org.springframework.web.cors.CorsUtils;
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final IUserService userService;
@Autowired
public WebSecurityConfig(IUserService userService) {
this.userService = userService;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/tech/health/check").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/logout-success").permitAll()
.anyRequest().authenticated()
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
.and().sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true)
.and().and().logout().invalidateHttpSession(true).clearAuthentication(true)
.logoutSuccessUrl("/logout-success").deleteCookies(Constant.COOKIE_NAME)
.and();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(new PasswordEncoder() {
@Override
public String encode(CharSequence rawPass) {
return PwdUtil.MD5(rawPass.toString().toLowerCase() + Constant.PASSWORD_SALT);
}
@Override
public boolean matches(CharSequence rawPass, String password) {
if (StringUtils.isEmpty(password)) {
return false;
}
return password.equals(PwdUtil.MD5(rawPass.toString().toLowerCase() + Constant.PASSWORD_SALT));
}
});
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}
package cn.quantgroup.customer.config.http.security;
import cn.quantgroup.customer.constant.Constant;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.http.CookieHttpSessionStrategy;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
import org.springframework.session.web.http.HttpSessionStrategy;
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = Constant.COOKIE_AND_SESSION_EXPIRE_TIMEOUT_SECONDS, redisNamespace = Constant.CUSTOMER_SESSION_NAMESPACE)
public class WebSessionConfig {
@Bean(name = "httpSessionStrategy")
public HttpSessionStrategy httpSessionStrategy(@Qualifier("cookieSerializer") CookieSerializer cookieSerializer) {
CookieHttpSessionStrategy strategy = new CookieHttpSessionStrategy();
strategy.setCookieSerializer(cookieSerializer);
return strategy;
}
@Bean(name = "cookieSerializer")
public CookieSerializer generateCookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName(Constant.COOKIE_NAME);
serializer.setCookiePath("/");
serializer.setDomainNamePattern("^.+?.(\\w+.[a-z]+)$");
serializer.setCookieMaxAge(Constant.COOKIE_AND_SESSION_EXPIRE_TIMEOUT_SECONDS);
return serializer;
}
}
package cn.quantgroup.customer.constant;
public interface Constant {
String PASSWORD_SALT = "_lkb";
String COOKIE_NAME = "SESSION";
String CUSTOMER_SESSION_NAMESPACE = "CUSTOMER:SESSION_NAMESPACE";
String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
int COOKIE_AND_SESSION_EXPIRE_TIMEOUT_SECONDS = 86400;
String LOGIN_SUCCESS = "登录成功";
String LOGOUT_SUCCESS = "登出成功";
String LOGIN_FAIL = "登录失败";
}
package cn.quantgroup.customer.entity;
import cn.quantgroup.customer.enums.AuthorityEnum;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;
import javax.persistence.*;
import java.util.Objects;
@Entity
@Table(name = "authorities")
@Getter
@Setter
@ToString
public class Authority implements GrantedAuthority {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "authority")
private AuthorityEnum authority;
@Override
public String getAuthority() {
if (Objects.isNull(authority)) {
return null;
}
return authority.getName();
}
public String getAuthorityMessage() {
if (Objects.isNull(authority)) {
return null;
}
return authority.getMessage();
}
}
package cn.quantgroup.customer.entity;
import cn.quantgroup.customer.enums.RoleTypeEnum;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
import java.sql.Timestamp;
@Entity
@Table(name = "role")
@Getter
@Setter
@ToString
public class Role {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "remark")
private String remark;
@Column(name = "role_type")
private RoleTypeEnum roleType;
@Column(name = "company_id")
private Long companyId;
@Column(name = "created_at")
private Timestamp createdAt;
@Column(name = "updated_at")
private Timestamp updatedAt;
}
package cn.quantgroup.customer.entity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
import java.sql.Timestamp;
@Entity
@Table(name = "role_authority")
@Getter
@Setter
@ToString
public class RoleAuthority {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "role_id", nullable = false)
private Long roleId;
@Column(name = "authority_id", nullable = false)
private Long authorityId;
@Column(name = "enable")
private Boolean enable;
@Column(name = "created_at")
private Timestamp createdAt;
@Column(name = "updated_at")
private Timestamp updatedAt;
}
package cn.quantgroup.customer.entity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.List;
@Entity
@Table(name = "user", uniqueConstraints = {@UniqueConstraint(columnNames = {"username"})})
@Getter
@Setter
@ToString
public class User implements Serializable, UserDetails {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username", nullable = false)
private String username;
@Column(name = "name", nullable = false)
private String name;
@Column(name = "password", nullable = false)
private String password;
@Column(name = "enable")
private Boolean enable;
@Column(name = "role")
private Long role;
@Column(name = "company_id")
private Long companyId;
@Column(name = "created_at")
private Timestamp createdAt;
@Column(name = "updated_at")
private Timestamp updatedAt;
@Transient
private List<? extends GrantedAuthority> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return this.enable;
}
}
package cn.quantgroup.customer.entity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
import java.io.Serializable;
import java.sql.Timestamp;
@Entity
@Table(name = "user_role")
@Getter
@Setter
@ToString
public class UserRole implements Serializable {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "user_id", nullable = false)
private Long userId;
@Column(name = "role_id", nullable = false)
private Long roleId;
@Column(name = "enable")
private Boolean enable;
@Column(name = "created_at")
private Timestamp createdAt;
@Column(name = "updated_at")
private Timestamp updatedAt;
}
package cn.quantgroup.customer.enums;
import lombok.Getter;
@Getter
public enum AuthorityEnum {
MODIFY_PHONE("MODIFY_PHONE", "修改手机号", 10);
private String name;
private int value;
private String message;
AuthorityEnum(String name, String message, int value) {
this.name = name;
this.value = value;
this.message = message;
}
}
package cn.quantgroup.customer.enums;
public enum RoleTypeEnum {
}
package cn.quantgroup.customer.repo;
import cn.quantgroup.customer.entity.Authority;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Collection;
import java.util.List;
public interface AuthorityRepo extends JpaRepository<Authority, Long> {
List<Authority> findByIdIn(Collection<Long> idList);
}
package cn.quantgroup.customer.repo;
import cn.quantgroup.customer.entity.RoleAuthority;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Collection;
import java.util.List;
public interface RoleAuthorityRepo extends JpaRepository<RoleAuthority, Long> {
List<RoleAuthority> findByRoleIdIn(Collection<Long> roleIdList);
}
package cn.quantgroup.customer.repo;
import cn.quantgroup.customer.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepo extends JpaRepository<User, Long> {
User findByUsername(String username);
}
package cn.quantgroup.customer.repo;
import cn.quantgroup.customer.entity.UserRole;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface UserRoleRepo extends JpaRepository<UserRole, Long> {
List<UserRole> findByUserId(Long userId);
}
package cn.quantgroup.customer.rest;
import java.io.Serializable;
public class JsonResult implements Serializable {
public static final Long API_INVOKE_UNEXPECTED_RESULT_CODE = 2L;
private static final Long SUCCESS_CODE = 0L;
private static final Long ERROR_STATE_CODE = 1L;
private static final Long SUCCESS_BUSSINESS_CODE = 0L;
private static final Long ERROR_BUSSINESS_CODE = 1L;
private static final long serialVersionUID = -1L;
private static final String ZERO_FILL_TEMPLATE = "%04d";
private static final String FLOAT_MONEY_ZERO_FILL = "%.2f";
private String msg = "";
// 0成功,1失败
private String code = "0000";
// 业务错误码
private String businessCode = "0000";
// 业务状态码
private String businessFlag = "0000";
private Object data = null;
public JsonResult() {
}
/**
* @param msg
* @param code
* @param data
*/
public JsonResult(String msg, Long code, Object data) {
this.msg = msg;
this.code = String.format(ZERO_FILL_TEMPLATE, code);
this.data = data;
}
public JsonResult(String msg, Long code, Object data, Long businessCode) {
this.msg = msg;
this.code = String.format(ZERO_FILL_TEMPLATE, code);
this.data = data;
this.businessCode = String.format(ZERO_FILL_TEMPLATE, businessCode);
}
public JsonResult(String msg, Long code, Object data, Long businessFlag, Long businessCode) {
this.msg = msg;
this.code = String.format(ZERO_FILL_TEMPLATE, code);
this.data = data;
this.businessCode = String.format(ZERO_FILL_TEMPLATE, businessCode);
this.businessFlag = String.format(ZERO_FILL_TEMPLATE, businessFlag);
}
public JsonResult(Object data) {
this.data = data;
}
/**
* 构造成功的JsonResult
*
* @param msg String
* @param data Object
* @return JsonResult
*/
public static JsonResult buildSuccessResult(String msg, Object data) {
return new JsonResult(msg, SUCCESS_CODE, data, SUCCESS_BUSSINESS_CODE);
}
public static JsonResult buildSuccessResult(String msg, Object data, Long businessId) {
return new JsonResult(msg, SUCCESS_CODE, data, businessId);
}
public static JsonResult buildSuccessResult(String msg, Object data, Long businessFlag, Long businessCode) {
return new JsonResult(msg, SUCCESS_CODE, data, businessFlag, businessCode);
}
/**
* 构造状态不正确的JsonResult
*
* @param msg String
* @param data Object
* @return JsonResult
*/
public static JsonResult buildErrorStateResult(String msg, Object data) {
return new JsonResult(msg, ERROR_STATE_CODE, data, ERROR_BUSSINESS_CODE);
}
public static JsonResult buildErrorStateResult(String msg, Object data, Long businessId) {
return new JsonResult(msg, ERROR_STATE_CODE, data, businessId);
}
public static JsonResult buildFatalErrorStateResult(String msg, Object data, Long businessId) {
return new JsonResult(msg, ERROR_STATE_CODE, data, businessId);
}
public static JsonResult buildApiInvokeUnexpectedResult(String msg, Object data) {
return new JsonResult(msg, SUCCESS_CODE, data, API_INVOKE_UNEXPECTED_RESULT_CODE);
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getBusinessCode() {
return businessCode;
}
public void setBusinessCode(String businessCode) {
this.businessCode = businessCode;
}
public String getBusinessFlag() {
return businessFlag;
}
public void setBusinessFlag(String businessFlag) {
this.businessFlag = businessFlag;
}
@Override
public String toString() {
return "JsonResult{" +
"businessCode='" + businessCode + '\'' +
", code='" + code + '\'' +
", data=" + data +
",businessFlag=" + businessFlag +
'}';
}
public boolean isSuccess() {
return "0000".equals(code) && "0000".equals(businessCode);
}
}
package cn.quantgroup.customer.rest;
import cn.quantgroup.customer.entity.User;
import cn.quantgroup.customer.rest.param.LoginParam;
import cn.quantgroup.customer.rest.vo.SimpleUserVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import static cn.quantgroup.customer.constant.Constant.*;
@Slf4j
@RestController
public class UserRest {
private final AuthenticationManager authenticationManager;
public UserRest(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@PostMapping(value = "/login")
public JsonResult login(@Valid @ModelAttribute LoginParam loginParam, HttpServletRequest request) {
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(loginParam.getUsername(), loginParam.getPassword());
Authentication authentication = authenticationManager.authenticate(authRequest);
SecurityContextHolder.getContext().setAuthentication(authentication);
HttpSession session = request.getSession();
session.setAttribute(SPRING_SECURITY_CONTEXT, SecurityContextHolder.getContext());
if (authentication != null && authentication.isAuthenticated()) {
User principal = (User) authentication.getPrincipal();
SimpleUserVo simpleUserVo = SimpleUserVo.parse(principal);
return JsonResult.buildSuccessResult(LOGIN_SUCCESS, simpleUserVo);
}
return JsonResult.buildErrorStateResult(LOGIN_FAIL, null);
}
@PostMapping(value = "/logout")
public JsonResult logout(@AuthenticationPrincipal User user) {
return JsonResult.buildSuccessResult(LOGOUT_SUCCESS, null);
}
@GetMapping(value = "/logout-success")
public JsonResult logoutSuccess() {
return JsonResult.buildSuccessResult(LOGOUT_SUCCESS, null);
}
}
package cn.quantgroup.customer.rest.param;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
public class LoginParam {
@NotNull(message = "用户名不能为空")
private String username;
@NotNull(message = "密码不能为空")
private String password;
}
package cn.quantgroup.customer.rest.vo;
import cn.quantgroup.customer.entity.User;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import java.util.List;
import java.util.stream.Collectors;
@Getter
@Setter
@Builder
public class SimpleUserVo {
private String username;
private List<String> authorities;
public static SimpleUserVo parse(User user) {
SimpleUserVo.SimpleUserVoBuilder builder = SimpleUserVo.builder();
builder.username(user.getUsername());
List<String> authorityList = user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
builder.authorities(authorityList);
return builder.build();
}
}
package cn.quantgroup.customer.service;
import cn.quantgroup.customer.entity.Authority;
import cn.quantgroup.customer.entity.RoleAuthority;
import cn.quantgroup.customer.entity.UserRole;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public interface IUserService extends UserDetailsService {
List<UserRole> findUserRoleByUserId(Long userId);
List<RoleAuthority> findRoleAuthorityByRoleIds(List<Long> roleIdList);
List<Authority> findAuthorityByAuthorityIds(List<Long> authorityIdList);
}
package cn.quantgroup.customer.service.impl;
import cn.quantgroup.customer.entity.Authority;
import cn.quantgroup.customer.entity.RoleAuthority;
import cn.quantgroup.customer.entity.User;
import cn.quantgroup.customer.entity.UserRole;
import cn.quantgroup.customer.repo.AuthorityRepo;
import cn.quantgroup.customer.repo.RoleAuthorityRepo;
import cn.quantgroup.customer.repo.UserRepo;
import cn.quantgroup.customer.repo.UserRoleRepo;
import cn.quantgroup.customer.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service("userService")
public class UserServiceImpl implements IUserService {
private final UserRepo userRepo;
private final UserRoleRepo userRoleRepo;
private final AuthorityRepo authorityRepo;
private final RoleAuthorityRepo roleAuthorityRepo;
@Autowired
public UserServiceImpl(UserRepo userRepo, UserRoleRepo userRoleRepo,
AuthorityRepo authorityRepo, RoleAuthorityRepo roleAuthorityRepo) {
this.userRepo = userRepo;
this.userRoleRepo = userRoleRepo;
this.authorityRepo = authorityRepo;
this.roleAuthorityRepo = roleAuthorityRepo;
}
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
User user = userRepo.findByUsername(userName);
if (user == null) {
throw new UsernameNotFoundException("user: " + userName + " do not exist!");
}
List<UserRole> userRoles = findUserRoleByUserId(user.getId());
if (CollectionUtils.isNotEmpty(userRoles)) {
List<RoleAuthority> roleAuthorityList = findRoleAuthorityByRoleIds(
userRoles.stream().map(UserRole::getRoleId).collect(Collectors.toList()));
if (CollectionUtils.isNotEmpty(roleAuthorityList)) {
List<Authority> authorities = findAuthorityByAuthorityIds(
roleAuthorityList.stream().map(RoleAuthority::getAuthorityId).collect(Collectors.toList()));
user.setAuthorities(authorities);
}
}
return user;
}
@Override
public List<UserRole> findUserRoleByUserId(Long userId) {
return userRoleRepo.findByUserId(userId);
}
@Override
public List<RoleAuthority> findRoleAuthorityByRoleIds(List<Long> roleIdList) {
return roleAuthorityRepo.findByRoleIdIn(roleIdList);
}
@Override
public List<Authority> findAuthorityByAuthorityIds(List<Long> authorityIdList) {
return authorityRepo.findByIdIn(authorityIdList);
}
}
package cn.quantgroup.customer.util;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
public class PwdUtil {
private static final char[] HEX_DIGITS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
public static String MD5(String s) {
try {
byte[] strTemp = s.getBytes(StandardCharsets.UTF_8);
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(strTemp);
byte[] md = mdTemp.digest();
int j = md.length;
char[] str = new char[j << 1];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = HEX_DIGITS[byte0 >>> 4 & 0xf];
str[k++] = HEX_DIGITS[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
return null;
}
}
}
app.id=customer-service
namespace=application,tech.common,tech.sleuth,tech.service.urls,tech.deploy,tech.msg.sdk
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<springProperty name="spring.application.name" source="spring.application.name"/>
<property name="LOG_LEVEL_PATTERN" value="%5p [${spring.application.name:-},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]"/>
<property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN:-%5p} --- [%thread] [%file:%line] %logger - %msg%n}"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/home/quant_group/logs/${spring.application.name:-application}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/home/quant_group/logs/${spring.application.name:-application}.log.%d{yyyy-MM-dd}</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<logger name="org.springframework" level="warn"/>
<logger name="org.hibernate" level="warn"/>
<logger name="org.apache" level="warn"/>
<logger name="ch.qos.logback" level="warn"/>
<root level="info">
<appender-ref ref="FILE"/>
</root>
</configuration>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/home/quant_group/logs/credit-league.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/home/quant_group/logs/credit-league.log.%d{yyyy-MM-dd}</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{ISO8601} [%thread] [%-5level] %logger - %msg%n</pattern>
</layout>
</appender>
<logger name="org.springframework" level="warn"/>
<logger name="org.mybatis" level="warn"/>
<logger name="org.apache" level="warn"/>
<logger name="ch.qos.logback" level="warn"/>
<logger name="root" level="INFO">
<appender-ref ref="FILE"/>
</logger>
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</configuration>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment