JWT Token

This commit is contained in:
Chantha 2020-05-19 16:51:38 +07:00
parent 0c0dffc6af
commit 4fce30fdc7
20 changed files with 253 additions and 39 deletions

View File

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: io.jsonwebtoken:jjwt:0.9.1">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/io/jsonwebtoken/jjwt/0.9.1/jjwt-0.9.1.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/io/jsonwebtoken/jjwt/0.9.1/jjwt-0.9.1-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/io/jsonwebtoken/jjwt/0.9.1/jjwt-0.9.1-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -2,27 +2,20 @@
<project version="4">
<component name="ChangeListManager">
<list default="true" id="47c3fbf4-4238-47d3-9ffa-fcd23da4e01e" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/.idea/libraries/Maven__org_attoparser_attoparser_2_0_5_RELEASE.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_thymeleaf_2_3_0_RELEASE.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/libraries/Maven__org_thymeleaf_extras_thymeleaf_extras_java8time_3_0_4_RELEASE.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/libraries/Maven__org_thymeleaf_thymeleaf_3_0_11_RELEASE.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/libraries/Maven__org_thymeleaf_thymeleaf_spring5_3_0_11_RELEASE.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/libraries/Maven__org_unbescape_unbescape_1_1_6_RELEASE.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/kotlin/com/chantha/jdbc/controller/UserController.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/resources/templates/register.html" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/libraries/Maven__io_jsonwebtoken_jjwt_0_9_1.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/kotlin/com/chantha/jdbc/utils/jwt/JwtAuthenticationEntryPoint.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/kotlin/com/chantha/jdbc/utils/jwt/JwtRequest.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/kotlin/com/chantha/jdbc/utils/jwt/JwtRequestFilter.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/kotlin/com/chantha/jdbc/utils/jwt/JwtResponse.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/kotlin/com/chantha/jdbc/utils/jwt/JwtTokenUtil.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/jdbc.iml" beforeDir="false" afterPath="$PROJECT_DIR$/jdbc.iml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pom.xml" beforeDir="false" afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/com/chantha/jdbc/config/WebConfig.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/com/chantha/jdbc/config/WebConfig.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/com/chantha/jdbc/security/User.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/com/chantha/jdbc/security/User.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/com/chantha/jdbc/security/UserDetailServiceImpl.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/com/chantha/jdbc/security/UserDetailServiceImpl.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/kotlin/com/chantha/jdbc/security/UserPrincipal.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/kotlin/com/chantha/jdbc/security/UserPrincipal.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/resources/application.properties" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/application.properties" afterDir="false" />
<change beforePath="$PROJECT_DIR$/target/classes/application.properties" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/application.properties" afterDir="false" />
<change beforePath="$PROJECT_DIR$/target/classes/com/chantha/jdbc/config/WebConfig.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/chantha/jdbc/config/WebConfig.class" afterDir="false" />
<change beforePath="$PROJECT_DIR$/target/classes/com/chantha/jdbc/security/User.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/chantha/jdbc/security/User.class" afterDir="false" />
<change beforePath="$PROJECT_DIR$/target/classes/com/chantha/jdbc/security/UserDetailServiceImpl.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/chantha/jdbc/security/UserDetailServiceImpl.class" afterDir="false" />
<change beforePath="$PROJECT_DIR$/target/classes/com/chantha/jdbc/security/UserPrincipal.class" beforeDir="false" afterPath="$PROJECT_DIR$/target/classes/com/chantha/jdbc/security/UserPrincipal.class" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -33,11 +26,11 @@
<option name="RECENT_TEMPLATES">
<list>
<option value="Interface" />
<option value="Class" />
<option value="Kotlin Class" />
<option value="Kotlin Interface" />
<option value="HTML File" />
<option value="Kotlin File" />
<option value="Class" />
</list>
</option>
</component>
@ -71,7 +64,7 @@
<property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="aspect.path.notification.shown" value="true" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/../springInterview" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/../spring-boot-react-oauth2-social-login-demo/spring-social" />
<property name="restartRequiresConfirmation" value="false" />
</component>
<component name="RecentsManager">
@ -112,7 +105,7 @@
<workItem from="1589774040527" duration="72000" />
<workItem from="1589788206287" duration="4024000" />
<workItem from="1589857721932" duration="2546000" />
<workItem from="1589862025987" duration="12626000" />
<workItem from="1589862025987" duration="17207000" />
</task>
<task id="LOCAL-00001" summary="&quot;Add Spring Security to project&quot;">
<created>1589796661163</created>
@ -140,6 +133,10 @@
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="690" y="276" key="#com.intellij.codeInsight.editorActions.RestoreReferencesDialog/0.0.1920.1040@0.0.1920.1040" timestamp="1589601961718" />
<state x="414" y="176" key="#com.intellij.execution.impl.EditConfigurationsDialog" timestamp="1589880612427">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="414" y="176" key="#com.intellij.execution.impl.EditConfigurationsDialog/0.0.1920.1040@0.0.1920.1040" timestamp="1589880612427" />
<state x="765" y="197" key="#com.intellij.ide.util.MemberChooser" timestamp="1589863580998">
<screen x="0" y="0" width="1920" height="1040" />
</state>
@ -160,42 +157,42 @@
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="508" y="29" key="CommitChangelistDialog2/0.0.1920.1040@0.0.1920.1040" timestamp="1589796659997" />
<state x="740" y="276" key="FileChooserDialogImpl" timestamp="1589873287289">
<state x="740" y="276" key="FileChooserDialogImpl" timestamp="1589879048291">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="740" y="276" key="FileChooserDialogImpl/0.0.1920.1040@0.0.1920.1040" timestamp="1589873287289" />
<state width="1877" height="420" key="GridCell.Tab.0.bottom" timestamp="1589876503739">
<state x="740" y="276" key="FileChooserDialogImpl/0.0.1920.1040@0.0.1920.1040" timestamp="1589879048291" />
<state width="1877" height="420" key="GridCell.Tab.0.bottom" timestamp="1589881169863">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="420" key="GridCell.Tab.0.bottom/0.0.1920.1040@0.0.1920.1040" timestamp="1589876503739" />
<state width="1877" height="420" key="GridCell.Tab.0.center" timestamp="1589876503738">
<state width="1877" height="420" key="GridCell.Tab.0.bottom/0.0.1920.1040@0.0.1920.1040" timestamp="1589881169863" />
<state width="1877" height="420" key="GridCell.Tab.0.center" timestamp="1589881169862">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="420" key="GridCell.Tab.0.center/0.0.1920.1040@0.0.1920.1040" timestamp="1589876503738" />
<state width="1877" height="420" key="GridCell.Tab.0.left" timestamp="1589876503738">
<state width="1877" height="420" key="GridCell.Tab.0.center/0.0.1920.1040@0.0.1920.1040" timestamp="1589881169862" />
<state width="1877" height="420" key="GridCell.Tab.0.left" timestamp="1589881169862">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="420" key="GridCell.Tab.0.left/0.0.1920.1040@0.0.1920.1040" timestamp="1589876503738" />
<state width="1877" height="420" key="GridCell.Tab.0.right" timestamp="1589876503739">
<state width="1877" height="420" key="GridCell.Tab.0.left/0.0.1920.1040@0.0.1920.1040" timestamp="1589881169862" />
<state width="1877" height="420" key="GridCell.Tab.0.right" timestamp="1589881169863">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="420" key="GridCell.Tab.0.right/0.0.1920.1040@0.0.1920.1040" timestamp="1589876503739" />
<state width="1877" height="420" key="GridCell.Tab.1.bottom" timestamp="1589876503739">
<state width="1877" height="420" key="GridCell.Tab.0.right/0.0.1920.1040@0.0.1920.1040" timestamp="1589881169863" />
<state width="1877" height="420" key="GridCell.Tab.1.bottom" timestamp="1589881169863">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="420" key="GridCell.Tab.1.bottom/0.0.1920.1040@0.0.1920.1040" timestamp="1589876503739" />
<state width="1877" height="420" key="GridCell.Tab.1.center" timestamp="1589876503739">
<state width="1877" height="420" key="GridCell.Tab.1.bottom/0.0.1920.1040@0.0.1920.1040" timestamp="1589881169863" />
<state width="1877" height="420" key="GridCell.Tab.1.center" timestamp="1589881169863">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="420" key="GridCell.Tab.1.center/0.0.1920.1040@0.0.1920.1040" timestamp="1589876503739" />
<state width="1877" height="420" key="GridCell.Tab.1.left" timestamp="1589876503739">
<state width="1877" height="420" key="GridCell.Tab.1.center/0.0.1920.1040@0.0.1920.1040" timestamp="1589881169863" />
<state width="1877" height="420" key="GridCell.Tab.1.left" timestamp="1589881169863">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="420" key="GridCell.Tab.1.left/0.0.1920.1040@0.0.1920.1040" timestamp="1589876503739" />
<state width="1877" height="420" key="GridCell.Tab.1.right" timestamp="1589876503739">
<state width="1877" height="420" key="GridCell.Tab.1.left/0.0.1920.1040@0.0.1920.1040" timestamp="1589881169863" />
<state width="1877" height="420" key="GridCell.Tab.1.right" timestamp="1589881169863">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="420" key="GridCell.Tab.1.right/0.0.1920.1040@0.0.1920.1040" timestamp="1589876503739" />
<state width="1877" height="420" key="GridCell.Tab.1.right/0.0.1920.1040@0.0.1920.1040" timestamp="1589881169863" />
<state width="1877" height="326" key="GridCell.Tab.2.bottom" timestamp="1589796703748">
<screen x="0" y="0" width="1920" height="1040" />
</state>
@ -224,9 +221,9 @@
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="616" y="240" key="run.anything.popup/0.0.1920.1040@0.0.1920.1040" timestamp="1589796621453" />
<state x="623" y="225" width="672" height="678" key="search.everywhere.popup" timestamp="1589874332627">
<state x="623" y="225" width="672" height="678" key="search.everywhere.popup" timestamp="1589877750441">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="623" y="225" width="672" height="678" key="search.everywhere.popup/0.0.1920.1040@0.0.1920.1040" timestamp="1589874332627" />
<state x="623" y="225" width="672" height="678" key="search.everywhere.popup/0.0.1920.1040@0.0.1920.1040" timestamp="1589877750441" />
</component>
</project>

View File

@ -176,5 +176,6 @@
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.6.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework:spring-test:5.2.6.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.xmlunit:xmlunit-core:2.6.4" level="project" />
<orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" />
</component>
</module>

View File

@ -77,6 +77,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
<build>

View File

@ -1,5 +1,6 @@
package com.chantha.jdbc.config;
import com.chantha.jdbc.utils.jwt.JwtRequestFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -13,6 +14,7 @@ import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
@ -21,7 +23,8 @@ public class WebConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Autowired
public WebConfig(UserDetailsService userDetailsService){
@ -41,6 +44,7 @@ public class WebConfig extends WebSecurityConfigurerAdapter {
.antMatchers("/register").permitAll()
.antMatchers("/**").hasAnyRole("ADMIN");
http.csrf().disable();
http.addFilterBefore(jwtRequestFilter,UsernamePasswordAuthenticationFilter.class);
}
@Bean

View File

@ -3,13 +3,14 @@ package com.chantha.jdbc.security
import org.springframework.beans.factory.annotation.Autowired
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
@Service
class UserDetailServiceImpl @Autowired constructor(private val userRepo: UserRepo):UserDetailsService {
@Throws(Exception::class)
@Throws(UsernameNotFoundException::class)
override fun loadUserByUsername(p0: String?): UserDetails {
val user=userRepo.findByUsername(p0!!)
val user = userRepo.findByUsername(p0!!)
return UserPrincipal(user)
}

View File

@ -0,0 +1,19 @@
package com.chantha.jdbc.utils.jwt;
import java.io.IOException;
import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = -7858869558953243875L;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}

View File

@ -0,0 +1,28 @@
package com.chantha.jdbc.utils.jwt;
import java.io.Serializable;
public class JwtRequest implements Serializable {
private static final long serialVersionUID = 5926468583005150707L;
private String username;
private String password;
//need default constructor for JSON Parsing
public JwtRequest()
{
}
public JwtRequest(String username, String password) {
this.setUsername(username);
this.setPassword(password);
}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -0,0 +1,63 @@
package com.chantha.jdbc.utils.jwt;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import com.chantha.jdbc.security.UserDetailServiceImpl;
import io.jsonwebtoken.ExpiredJwtException;
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private UserDetailServiceImpl jwtUserDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
// JWT Token is in the form "Bearer token". Remove Bearer word and get
// only the Token
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}
// Once we get the token validate it.
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
// if token is valid configure Spring Security to manually set
// authentication
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the
// Spring Security Configurations successfully.
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}

View File

@ -0,0 +1,16 @@
package com.chantha.jdbc.utils.jwt;
import java.io.Serializable;
public class JwtResponse implements Serializable {
private static final long serialVersionUID = -8091879091924046844L;
private final String jwttoken;
public JwtResponse(String jwttoken) {
this.jwttoken = jwttoken;
}
public String getToken() {
return this.jwttoken;
}
}

View File

@ -0,0 +1,63 @@
package com.chantha.jdbc.utils.jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
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.function.Function;
@Component
public class JwtTokenUtil implements Serializable {
private static final long serialVersionUID = -2550185165626007488L;
public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60;
@Value("${jwt.secret}")
private String secret;
//retrieve username from jwt token
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
//retrieve expiration date from jwt token
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);
}
//for retrieveing any information from token we will need the secret key
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
//check if the token has expired
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
//generate token for user
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return doGenerateToken(claims, userDetails.getUsername());
}
//while creating the token -
//1. Define claims of the token, like Issuer, Expiration, Subject, and the ID
//2. Sign the JWT using the HS512 algorithm and secret key.
//3. According to JWS Compact Serialization(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1)
// compaction of the JWT to a URL-safe string
private String doGenerateToken(Map<String, Object> claims, String subject) {
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
.signWith(SignatureAlgorithm.HS512, secret).compact();
}
//validate token
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}

View File

@ -7,6 +7,8 @@ spring.jpa.open-in-view=true
server.port=8081
spring.thymeleaf.cache=true
jwt.secret=javainuse

View File

@ -7,6 +7,8 @@ spring.jpa.open-in-view=true
server.port=8081
spring.thymeleaf.cache=true
jwt.secret=javainuse