xiongzhu 3 tahun lalu
induk
melakukan
4da02c55ee

+ 40 - 0
pom.xml

@@ -53,6 +53,46 @@
 			<artifactId>druid-spring-boot-starter</artifactId>
 			<version>1.2.8</version>
 		</dependency>
+
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-text</artifactId>
+			<version>1.3</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-security</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>io.jsonwebtoken</groupId>
+			<artifactId>jjwt</artifactId>
+			<version>0.9.1</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-cache</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-redis</artifactId>
+		</dependency>
+
+		<!-- swagger -->
+		<dependency>
+			<groupId>io.springfox</groupId>
+			<artifactId>springfox-swagger2</artifactId>
+			<version>2.9.1</version>
+		</dependency>
+
+		<dependency>
+			<groupId>io.springfox</groupId>
+			<artifactId>springfox-swagger-ui</artifactId>
+			<version>2.9.1</version>
+		</dependency>
 	</dependencies>
 
 	<build>

+ 4 - 0
src/main/java/com/example/jpatest/JpatestApplication.java

@@ -2,8 +2,12 @@ package com.example.jpatest;
 
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
 
 @SpringBootApplication
+@EnableCaching
+@EnableJpaAuditing
 public class JpatestApplication {
 
 	public static void main(String[] args) {

+ 80 - 0
src/main/java/com/example/jpatest/domain/BaseEntityNoID.java

@@ -0,0 +1,80 @@
+package com.example.jpatest.domain;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.springframework.data.annotation.CreatedBy;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedBy;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import javax.persistence.EntityListeners;
+import javax.persistence.MappedSuperclass;
+import java.time.LocalDateTime;
+
+@MappedSuperclass
+@EntityListeners(AuditingEntityListener.class)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(value = {"hibernateLazyInitializer"}, ignoreUnknown = true)
+public abstract class BaseEntityNoID {
+    @JsonIgnore
+    @CreatedBy
+    private String createdBy;
+
+    @JsonIgnore
+    @CreatedDate
+    private LocalDateTime createdAt;
+
+    @JsonIgnore
+    @LastModifiedBy
+    private String modifiedBy;
+
+    @JsonIgnore
+    @LastModifiedDate
+    private LocalDateTime modifiedAt;
+
+    private boolean del;
+
+    public String getCreatedBy() {
+        return createdBy;
+    }
+
+    public void setCreatedBy(String createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    @JsonProperty("createdAt")
+    public LocalDateTime getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(LocalDateTime createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    public String getModifiedBy() {
+        return modifiedBy;
+    }
+
+    public void setModifiedBy(String modifiedBy) {
+        this.modifiedBy = modifiedBy;
+    }
+
+    public LocalDateTime getModifiedAt() {
+        return modifiedAt;
+    }
+
+    public void setModifiedAt(LocalDateTime modifiedAt) {
+        this.modifiedAt = modifiedAt;
+    }
+
+    public boolean isDel() {
+        return del;
+    }
+
+    public void setDel(boolean del) {
+        this.del = del;
+    }
+}

+ 143 - 0
src/main/java/com/example/jpatest/domain/User.java

@@ -0,0 +1,143 @@
+package com.example.jpatest.domain;
+
+import com.example.jpatest.enums.AuthStatus;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.annotations.ApiModel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@Entity
+@Table(indexes = {
+        @Index(columnList = "phone", unique = true, name = "user_index_phone"),
+        @Index(columnList = "collectionId"),
+        @Index(columnList = "collectionInvitor"),
+        @Index(columnList = "admin"),
+        @Index(columnList = "minter"),
+        @Index(columnList = "createdAt"),
+        @Index(columnList = "settleAccountId")
+})
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@ApiModel(value = "用户", description = "用户")
+public class User extends BaseEntityNoID implements Serializable {
+
+    @Id
+//    @GenericGenerator(
+//            name = "some_column_name_id_generator",
+//            strategy = "com.github.marschall.hibernate.batchsequencegenerator.BatchSequenceGenerator",
+//            parameters = {
+//                    @org.hibernate.annotations.Parameter(name = "sequence", value = "user_sequence"),
+//                    @org.hibernate.annotations.Parameter(name = "fetch_size", value = "50")
+//            })
+//    @GeneratedValue(generator = "user_id_generator")
+
+    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")
+    @SequenceGenerator(name = "user_seq", sequenceName = "user_seq", allocationSize = 100, initialValue = 1890000)
+//    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    private boolean del = false;
+
+    private String createdBy;
+
+    private LocalDateTime createdAt;
+
+    private String modifiedBy;
+
+    private LocalDateTime modifiedAt;
+
+    //    @Pattern(regexp = Constants.Regex.USERNAME)
+    @Column(nullable = false, unique = true)
+    private String username;
+
+    private String nickname;
+
+    private String avatar;
+
+    private String password;
+
+    private String openId;
+
+    private String sex;
+
+    private String language;
+
+    private String city;
+
+    private String province;
+
+    private String country;
+
+    private String phone;
+
+    private String email;
+
+    private int follows;
+
+    private int followers;
+
+    private int sales;
+
+    @Column(columnDefinition = "TEXT")
+    private String intro;
+
+    private String bg;
+
+    private Long authId;
+
+    @Enumerated(EnumType.STRING)
+    private AuthStatus authStatus;
+
+    private String nftAccount;
+
+    @JsonIgnore
+    private String kmsId;
+
+    @JsonIgnore
+    private String publicKey;
+
+    @JsonIgnore
+    private String tradeCode;
+
+    private boolean admin;
+
+    @Column(precision = 10, scale = 2)
+    private BigDecimal shareRatio;
+
+    private String memberId;
+
+    private String settleAccountId;
+
+    private String invitorName;
+
+    private String invitorPhone;
+
+    private String inviteCode;
+
+    private Long collectionInvitor;
+
+    private Long collectionId;
+
+    private int level;
+
+    private int vipPurchase;
+
+    private boolean minter;
+
+    @Column(columnDefinition = "bit default false")
+    private boolean useCollectionPic;
+
+    @Column(columnDefinition = "int(11) default 0")
+    private int vipPoint = 0;
+
+    private Boolean riskWarning;
+}

+ 18 - 0
src/main/java/com/example/jpatest/enums/AuthStatus.java

@@ -0,0 +1,18 @@
+package com.example.jpatest.enums;
+
+public enum AuthStatus {
+    NOT_AUTH("未认证"),
+    PENDING("认证中"),
+    SUCCESS("已认证"),
+    FAIL("失败");
+
+    private final String description;
+
+    AuthStatus(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

+ 9 - 0
src/main/java/com/example/jpatest/repo/UserRepo.java

@@ -0,0 +1,9 @@
+package com.example.jpatest.repo;
+
+import com.example.jpatest.domain.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface UserRepo extends JpaRepository<User, Long> {
+
+    User findByUsernameAndDelFalse(String username);
+}

+ 25 - 0
src/main/java/com/example/jpatest/security/JwtAuthenticationEntryPoint.java

@@ -0,0 +1,25 @@
+package com.example.jpatest.security;
+
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.Serializable;
+
+@Component
+public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
+
+    private static final long serialVersionUID = -8970718410437077606L;
+
+    @Override
+    public void commence(HttpServletRequest request,
+                         HttpServletResponse response,
+                         AuthenticationException authException) throws IOException {
+        // This is invoked when user tries to access a secured REST resource without supplying any credentials
+        // We should just send a 401 Unauthorized response because there is no 'login page' to redirect to
+        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "授权失败");
+    }
+}

+ 12 - 0
src/main/java/com/example/jpatest/security/JwtConfig.java

@@ -0,0 +1,12 @@
+package com.example.jpatest.security;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@Data
+@ConfigurationProperties(prefix = "jwt")
+public class JwtConfig {
+    private String secret;
+    private String header;
+    private Long   expiration;
+}

+ 126 - 0
src/main/java/com/example/jpatest/security/JwtTokenUtil.java

@@ -0,0 +1,126 @@
+package com.example.jpatest.security;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Clock;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.impl.DefaultClock;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.stereotype.Component;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+
+@Component
+public class JwtTokenUtil implements Serializable {
+
+    private static final long serialVersionUID = -3301605591108950415L;
+
+    static final String CLAIM_KEY_USERNAME = "sub";
+    static final String CLAIM_KEY_CREATED  = "iat";
+
+    private Clock     clock = DefaultClock.INSTANCE;
+    private JwtConfig jwtConfig;
+
+    public JwtTokenUtil(JwtConfig jwtConfig) {
+        this.jwtConfig = jwtConfig;
+    }
+
+    public String getUsernameFromToken(String token) {
+        return getClaimFromToken(token, Claims::getSubject);
+    }
+
+    public Date getIssuedAtDateFromToken(String token) {
+        return getClaimFromToken(token, Claims::getIssuedAt);
+    }
+
+    public Date getExpirationDateFromToken(String token) {
+        return getClaimFromToken(token, Claims::getExpiration);
+    }
+
+    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
+        final Claims claims = getAllClaimsFromToken(token);
+        return claimsResolver.apply(claims);
+    }
+
+    private Claims getAllClaimsFromToken(String token) {
+        return Jwts.parser()
+                .setSigningKey(jwtConfig.getSecret())
+                .parseClaimsJws(token)
+                .getBody();
+    }
+
+    private Boolean isTokenExpired(String token) {
+        final Date expiration = getExpirationDateFromToken(token);
+        return expiration.before(clock.now());
+    }
+
+    private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
+        return false;
+    }
+
+    private Boolean ignoreTokenExpiration(String token) {
+        // here you specify tokens, for that the expiration is ignored
+        return false;
+    }
+
+    public String generateToken(UserDetails userDetails) {
+        JwtUser jwtUser = (JwtUser) userDetails;
+        Map<String, Object> claims = new HashMap<>();
+        String token = doGenerateToken(claims, userDetails.getUsername());
+        return token;
+    }
+
+    public String doGenerateToken(Map<String, Object> claims, String subject) {
+        final Date createdDate = clock.now();
+        final Date expirationDate = calculateExpirationDate(createdDate);
+
+        return Jwts.builder()
+                .setClaims(claims)
+                .setSubject(subject)
+                .setIssuedAt(createdDate)
+                .setExpiration(expirationDate)
+                .signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret())
+                .compact();
+    }
+
+    public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
+        final Date created = getIssuedAtDateFromToken(token);
+        return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset)
+                && (!isTokenExpired(token) || ignoreTokenExpiration(token));
+    }
+
+    public String refreshToken(String token) {
+        final Date createdDate = clock.now();
+        final Date expirationDate = calculateExpirationDate(createdDate);
+
+        final Claims claims = getAllClaimsFromToken(token);
+        claims.setIssuedAt(createdDate);
+        claims.setExpiration(expirationDate);
+
+        return Jwts.builder()
+                .setClaims(claims)
+                .signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret())
+                .compact();
+    }
+
+    public Boolean validateToken(String token, UserDetails userDetails) {
+        JwtUser user = (JwtUser) userDetails;
+        final String username = getUsernameFromToken(token);
+        final Date created = getIssuedAtDateFromToken(token);
+        //final Date expiration = getExpirationDateFromToken(token);
+        return (
+                username.equals(user.getUsername())
+                        && !isTokenExpired(token)
+                        && !isCreatedBeforeLastPasswordReset(created, user.getLastPasswordResetDate())
+        );
+    }
+
+    private Date calculateExpirationDate(Date createdDate) {
+        return new Date(createdDate.getTime() + jwtConfig.getExpiration() * 1000);
+    }
+}

+ 81 - 0
src/main/java/com/example/jpatest/security/JwtUser.java

@@ -0,0 +1,81 @@
+package com.example.jpatest.security;
+
+import com.example.jpatest.domain.User;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.time.ZoneId;
+import java.util.Collection;
+import java.util.Date;
+
+/**
+ * Created by stephan on 20.03.16.
+ */
+public class JwtUser implements UserDetails {
+
+    private static final long serialVersionUID = 5803985158027956021L;
+
+    private final Collection<? extends GrantedAuthority> authorities;
+    private final Date                                   lastPasswordResetDate;
+    private       User                                   user;
+
+    public JwtUser(User user, Collection<? extends GrantedAuthority> authorities) {
+        this.authorities = authorities;
+        this.lastPasswordResetDate = Date.from(user.getCreatedAt().atZone(ZoneId.systemDefault()).toInstant());
+        this.user = user;
+    }
+
+    @JsonIgnore
+    public Long getId() {
+        return user.getId();
+    }
+
+    @Override
+    public String getUsername() {
+        return user.getUsername();
+    }
+
+    @JsonIgnore
+    @Override
+    public boolean isAccountNonExpired() {
+        return true;
+    }
+
+    @JsonIgnore
+    @Override
+    public boolean isAccountNonLocked() {
+        return true;
+    }
+
+    @JsonIgnore
+    @Override
+    public boolean isCredentialsNonExpired() {
+        return true;
+    }
+
+    @JsonIgnore
+    @Override
+    public String getPassword() {
+        return user.getPassword();
+    }
+
+    @Override
+    public Collection<? extends GrantedAuthority> getAuthorities() {
+        return authorities;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return !user.isDel();
+    }
+
+    @JsonIgnore
+    public Date getLastPasswordResetDate() {
+        return lastPasswordResetDate;
+    }
+
+    public User getUser() {
+        return user;
+    }
+}

+ 26 - 0
src/main/java/com/example/jpatest/security/JwtUserDetailsService.java

@@ -0,0 +1,26 @@
+package com.example.jpatest.security;
+
+import com.example.jpatest.domain.User;
+import com.example.jpatest.repo.UserRepo;
+import lombok.AllArgsConstructor;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+@AllArgsConstructor
+@Service("jwtUserDetailsService")
+public class JwtUserDetailsService implements UserDetailsService {
+    private UserRepo userRepo;
+
+    @Override
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+        User user = userRepo.findByUsernameAndDelFalse(username);
+
+        if (user == null) {
+            throw new UsernameNotFoundException(String.format("No user found with username '%s'.", username));
+        } else {
+            return JwtUserFactory.create(user);
+        }
+    }
+}

+ 17 - 0
src/main/java/com/example/jpatest/security/JwtUserFactory.java

@@ -0,0 +1,17 @@
+package com.example.jpatest.security;
+
+import com.example.jpatest.domain.User;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+
+import java.util.Collections;
+
+public final class JwtUserFactory {
+
+    private JwtUserFactory() {
+    }
+
+    public static JwtUser create(User user) {
+        return new JwtUser(user, Collections.singleton(new SimpleGrantedAuthority("user")));
+    }
+
+}

+ 145 - 0
src/main/java/com/example/jpatest/security/WebSecurityConfig.java

@@ -0,0 +1,145 @@
+package com.example.jpatest.security;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.AuthenticationManager;
+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.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+@EnableConfigurationProperties({JwtConfig.class})
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+    private final JwtAuthenticationEntryPoint unauthorizedHandler;
+    private final UserDetailsService          userDetailsService;
+    private final String                      tokenHeader;
+
+    public WebSecurityConfig(JwtAuthenticationEntryPoint unauthorizedHandler,
+                             @Qualifier("jwtUserDetailsService") UserDetailsService userDetailsService,
+                             @Value("${jwt.header}") String tokenHeader) {
+        this.unauthorizedHandler = unauthorizedHandler;
+        this.userDetailsService = userDetailsService;
+        this.tokenHeader = tokenHeader;
+    }
+
+    @Override
+    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+        auth.userDetailsService(userDetailsService)
+                .passwordEncoder(passwordEncoderBean());
+    }
+
+    @Bean
+    public PasswordEncoder passwordEncoderBean() {
+        return new BCryptPasswordEncoder();
+    }
+
+    @Bean
+    @Override
+    public AuthenticationManager authenticationManagerBean() throws Exception {
+        return super.authenticationManagerBean();
+    }
+
+    @Override
+    protected void configure(HttpSecurity httpSecurity) throws Exception {
+        // We don't need CSRF for this example
+        httpSecurity.csrf().disable()
+                .cors().and()
+                // dont authenticate this particular request
+                .authorizeRequests()
+                //swagger-ui放行路径
+                .antMatchers("/v2/api-docs", "/swagger-ui.html", "/swagger-resources/**", "/webjars/**").permitAll()
+                .antMatchers("/user/register").permitAll()
+                .antMatchers("/upload/**").permitAll()
+                .antMatchers("/files/**").permitAll()
+                .antMatchers("/static/**").permitAll()
+                .antMatchers("/auth/**").permitAll()
+                .antMatchers("/captcha/**").permitAll()
+                .antMatchers("/admin/**").permitAll()
+                .antMatchers("/systemVariable/all").permitAll()
+                .antMatchers("/**/excel").permitAll()
+                .antMatchers("/wx/**").permitAll()
+                .antMatchers("/sms/sendVerify").permitAll()
+                .antMatchers("/sms/verify").permitAll()
+                .antMatchers("/error").permitAll()
+                .antMatchers("/401").permitAll()
+                .antMatchers("/404").permitAll()
+                .antMatchers("/500").permitAll()
+                .antMatchers("/MP_verify*").permitAll()
+                .antMatchers("/payOrder/**").permitAll()
+                .antMatchers("/notify/**").permitAll()
+                .antMatchers("/banner/all").permitAll()
+                .antMatchers("/collection/all").permitAll()
+                .antMatchers("/collection/get/**").permitAll()
+                .antMatchers("/asset/get/**").permitAll()
+                .antMatchers("/asset/tokenHistory").permitAll()
+                .antMatchers("/user/all").permitAll()
+                .antMatchers("/user/get/*").permitAll()
+                .antMatchers("/news/all").permitAll()
+                .antMatchers("/news/get/*").permitAll()
+                .antMatchers("/user/forgotPassword").permitAll()
+                .antMatchers("/sysConfig/get/*").permitAll()
+                .antMatchers("/sysConfig/getDecimal/*").permitAll()
+                .antMatchers("/user/code2openId").permitAll()
+                .antMatchers("/blindBoxItem/all").permitAll()
+                .antMatchers("/collection/recommend").permitAll()
+                .antMatchers("/order/**/status").permitAll()
+                .antMatchers("/order/checkLimit").permitAll()
+                .antMatchers("/mintOrder/**/status").permitAll()
+                .antMatchers("/activity/all").permitAll()
+                .antMatchers("/activity/get/*").permitAll()
+                .antMatchers("/mintActivity/all").permitAll()
+                .antMatchers("/mintActivity/get/**").permitAll()
+                .antMatchers("/purchaseLevel/all").permitAll()
+                .antMatchers("/purchaseLevel/get/**").permitAll()
+                .antMatchers("/appVersion/checkIosReview").permitAll()
+                .antMatchers("/appVersion/checkAndroidReview").permitAll()
+                .antMatchers("/news/all").permitAll()
+                .antMatchers("/news/get/**").permitAll()
+                .antMatchers("/druid/**").permitAll()
+                .antMatchers("/identityAuth/autoAuth").permitAll()
+                .antMatchers("/statistic/weekTop").permitAll()
+                .antMatchers("/showroom/all").permitAll()
+                .antMatchers("/showroom/get/**").permitAll()
+                .antMatchers("/testClass/**").permitAll()
+                // all other requests need to be authenticated
+                .anyRequest().authenticated().and()
+                // make sure we use stateless session; session won't be used to
+                // store user's state.
+                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
+                .and().sessionManagement()
+                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
+        // Add a filter to validate the tokens with every request
+    }
+
+    @Override
+    public void configure(WebSecurity web) throws Exception {
+        // AuthenticationTokenFilter will ignore the below paths
+        web.ignoring()
+                .antMatchers("/auth/**")
+
+                // allow anonymous resource requests
+                .and()
+                .ignoring()
+                .antMatchers(
+                        HttpMethod.GET,
+                        "/**",
+                        "/*.html",
+                        "/**/favicon.ico",
+                        "/**/*.html",
+                        "/**/*.css",
+                        "/**/*.js"
+                );
+    }
+}

+ 23 - 0
src/main/java/com/example/jpatest/web/UserController.java

@@ -0,0 +1,23 @@
+package com.example.jpatest.web;
+
+import com.example.jpatest.domain.User;
+import com.example.jpatest.repo.UserRepo;
+import lombok.AllArgsConstructor;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@AllArgsConstructor
+public class UserController {
+    private final UserRepo userRepo;
+
+    @GetMapping("/user/save")
+    public Object save() {
+        User user = new User();
+        user.setPhone(RandomStringUtils.randomNumeric(12));
+        user.setUsername(RandomStringUtils.randomAlphabetic(32));
+        userRepo.save(user);
+        return user;
+    }
+}

+ 77 - 51
src/main/resources/application.yaml

@@ -19,59 +19,85 @@ spring:
     url: jdbc:mysql://rm-wz9sc79f5255780opqo.mysql.rds.aliyuncs.com/raex_test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8
     username: raex_server
     password: tetQsjw!u4!c5$URduo7BH
-  druid:
-    # ????????
-    # ???????????
-    initial-size: 5
-    min-idle: 20
-    maxActive: 3000
-    # ?????????????
-    maxWait: 60000
-    # ??????????
-    use-unfair-lock: true
-    # ???????????????????????????????
-    timeBetweenEvictionRunsMillis: 30000
-    # ??????????????????????
-    minEvictableIdleTimeMillis: 300000
-    validationQuery: SELECT 1
-    testWhileIdle: true
-    testOnBorrow: false
-    testOnReturn: false
-    # ??PSCache??????????PSCache???
-    poolPreparedStatements: true
-    maxPoolPreparedStatementPerConnectionSize: 20
-    query-timeout: 300000
-    # ?????????filters????????sql?????'wall'?????
-    filters: stat,wall,slf4j
-    # ??connectProperties?????mergeSql????SQL??
-    connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
-    remove-abandoned: true
-    remove-abandoned-timeout: 1800
-    log-abandoned: true
-    # ????
-    keep-alive: true
-    # ??????
-    keep-alive-between-time-millis: 60000
-    # ??DruidStatFilter
-    web-stat-filter:
-      enabled: true
-      url-pattern: "/*"
-      exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
-    # ??DruidStatViewServlet
-    stat-view-servlet:
-      enabled: true
-      url-pattern: "/druid/*"
-      login-username: admin
-      login-password: 3edc#EDC
-    filter:
-      wall:
+    druid:
+      # ????????
+      # ???????????
+      initial-size: 5
+      min-idle: 20
+      maxActive: 3000
+      # ?????????????
+      maxWait: 60000
+      # ??????????
+      use-unfair-lock: true
+      # ???????????????????????????????
+      timeBetweenEvictionRunsMillis: 30000
+      # ??????????????????????
+      minEvictableIdleTimeMillis: 300000
+      validationQuery: SELECT 1
+      testWhileIdle: true
+      testOnBorrow: false
+      testOnReturn: false
+      # ??PSCache??????????PSCache???
+      poolPreparedStatements: true
+      maxPoolPreparedStatementPerConnectionSize: 20
+      query-timeout: 300000
+      # ?????????filters????????sql?????'wall'?????
+      filters: stat,wall,slf4j
+      # ??connectProperties?????mergeSql????SQL??
+      connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
+      remove-abandoned: true
+      remove-abandoned-timeout: 1800
+      log-abandoned: true
+      # ????
+      keep-alive: true
+      # ??????
+      keep-alive-between-time-millis: 60000
+      # ??DruidStatFilter
+      web-stat-filter:
         enabled: true
-        config:
-          condition-and-alway-false-allow: true
-          condition-and-alway-true-allow: true
-          select-where-alway-true-check: false
+        url-pattern: "/*"
+        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
+      # ??DruidStatViewServlet
+      stat-view-servlet:
+        enabled: true
+        url-pattern: "/druid/*"
+        login-username: admin
+        login-password: 3edc#EDC
+      filter:
+        wall:
+          enabled: true
+          config:
+            condition-and-alway-false-allow: true
+            condition-and-alway-true-allow: true
+            select-where-alway-true-check: false
   jpa:
     database: MySQL
-    database_platform: org.hibernate.dialect.MySQL5InnoDBDialect
+    database-platform: org.hibernate.dialect.MySQL8Dialect
     hibernate:
       ddl_auto: update
+    properties:
+      hibernate:
+        dialect: org.hibernate.dialect.MySQL8Dialect
+        storage_engine: innodb
+        enable_lazy_load_no_trans: true
+        order_inserts: true
+        order_updates: true
+        fetch_size: 400
+        jdbc:
+          batch_size: 30
+    open-in-view: true
+  cache:
+    type: redis
+  redis:
+    host: 120.78.171.194
+    database: 0
+    password: jV%93RtjUx82Tp
+    lettuce:
+      pool:
+        max_active: 50
+        max_idle: 50
+        min_idle: 0
+jwt:
+  secret: XvAD0kboD76Dpebm
+  header: Authorization
+  expiration: 2592000 #30days

+ 170 - 0
src/main/resources/logback-spring.xml

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
+
+    <springProfile name="dev">
+        <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+            <encoder>
+                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+            </encoder>
+        </appender>
+        <root level="DEBUG">
+            <appender-ref ref="CONSOLE"/>
+        </root>
+        <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>
+        <!--swagger 类型转换异常日志去除-->
+        <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>
+        <logger name="org.freemarker" level="DEBUG"/>
+
+<!--        <logger name="druid.sql.Statement" level="debug">-->
+<!--        </logger>-->
+<!--        <logger name="druid.sql.DataSource" level="debug">-->
+<!--        </logger>-->
+<!--        <logger name="druid.sql.Connection" level="debug">-->
+<!--        </logger>-->
+<!--        <logger name="druid.sql.ResultSet" level="debug">-->
+<!--        </logger>-->
+
+        <!--        <logger name="org.hibernate.SQL" level="DEBUG"/>-->
+        <!--        <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>-->
+        <!--        <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG"/>-->
+        <!--        <logger name="org.springframework.jdbc.core.StatementCreatorUtils" level="TRACE"/>-->
+        <!--        <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>-->
+        <!--        <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>-->
+    </springProfile>
+
+    <springProfile name="local">
+        <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+            <encoder>
+                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+            </encoder>
+        </appender>
+        <root level="INFO">
+            <appender-ref ref="CONSOLE"/>
+        </root>
+        <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>
+        <!--swagger 类型转换异常日志去除-->
+        <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>
+        <logger name="org.freemarker" level="DEBUG"/>
+
+        <!-- dingding log -->
+        <appender name="HTTP_INVOKE_LOGGER_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
+            <param name="append" value="true"/>
+            <param name="encoding" value="UTF-8"/>
+            <param name="threshold" value="INFO"/>
+            <encoder>
+                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
+            </encoder>
+        </appender>
+
+        <!--        <logger name="org.hibernate.SQL" level="DEBUG"/>-->
+        <!--        <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>-->
+        <!--        <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG"/>-->
+        <!--        <logger name="org.springframework.jdbc.core.StatementCreatorUtils" level="TRACE"/>-->
+        <!--        <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>-->
+        <!--        <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>-->
+    </springProfile>
+
+    <springProfile name="test">
+        <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+            <encoder>
+                <pattern>${FILE_LOG_PATTERN}</pattern>
+            </encoder>
+            <file>app.log</file>
+            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+                <fileNamePattern>app.log.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
+                <maxFileSize>100MB</maxFileSize>
+                <maxHistory>60</maxHistory>
+            </rollingPolicy>
+        </appender>
+        <root level="INFO">
+            <appender-ref ref="FILE"/>
+        </root>
+        <!--        <logger name="org.hibernate.SQL" level="DEBUG"/>-->
+        <!--        <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>-->
+        <!--        <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG"/>-->
+        <!--        <logger name="org.springframework.jdbc.core.StatementCreatorUtils" level="TRACE"/>-->
+        <!--        <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>-->
+        <!--        <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>-->
+    </springProfile>
+
+    <springProfile name="test1">
+        <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+            <encoder>
+                <pattern>${FILE_LOG_PATTERN}</pattern>
+            </encoder>
+            <file>app.log</file>
+            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+                <fileNamePattern>app.log.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
+                <maxFileSize>100MB</maxFileSize>
+                <maxHistory>60</maxHistory>
+            </rollingPolicy>
+        </appender>
+        <root level="INFO">
+            <appender-ref ref="FILE"/>
+        </root>
+        <!--        <logger name="org.hibernate.SQL" level="DEBUG"/>-->
+        <!--        <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>-->
+        <!--        <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG"/>-->
+        <!--        <logger name="org.springframework.jdbc.core.StatementCreatorUtils" level="TRACE"/>-->
+        <!--        <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>-->
+        <!--        <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>-->
+    </springProfile>
+
+    <springProfile name="prod">
+        <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+            <encoder>
+                <pattern>${FILE_LOG_PATTERN}</pattern>
+            </encoder>
+            <file>app.log</file>
+            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+                <fileNamePattern>app.log.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
+                <maxFileSize>100MB</maxFileSize>
+                <maxHistory>60</maxHistory>
+            </rollingPolicy>
+        </appender>
+        <root level="INFO">
+            <appender-ref ref="FILE"/>
+        </root>
+        <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>
+        <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>
+    </springProfile>
+
+    <springProfile name="notify">
+        <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+            <encoder>
+                <pattern>${FILE_LOG_PATTERN}</pattern>
+            </encoder>
+            <file>app.log</file>
+            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+                <fileNamePattern>app.log.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
+                <maxFileSize>100MB</maxFileSize>
+                <maxHistory>60</maxHistory>
+            </rollingPolicy>
+        </appender>
+        <root level="INFO">
+            <appender-ref ref="FILE"/>
+        </root>
+        <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>
+        <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>
+    </springProfile>
+
+    <springProfile name="notifytest">
+        <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+            <encoder>
+                <pattern>${FILE_LOG_PATTERN}</pattern>
+            </encoder>
+            <file>app.log</file>
+            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+                <fileNamePattern>app.log.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
+                <maxFileSize>100MB</maxFileSize>
+                <maxHistory>60</maxHistory>
+            </rollingPolicy>
+        </appender>
+        <root level="INFO">
+            <appender-ref ref="FILE"/>
+        </root>
+        <logger name="cn.binarywang.wx.miniapp" level="DEBUG"/>
+        <logger name="io.swagger.models.parameters.AbstractSerializableParameter" level="ERROR"/>
+    </springProfile>
+</configuration>