Creating an Application quickly
Using Springboot, Maven, JDK8, quickly create a Web application.
Basic access class written
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/ * * * *@authorLittle hidden Lele *@since2020/11/8 verily * /
@RestController
@RequestMapping("/api")
public class HelloController {
@GetMapping("/hello")
public String hello(a) {
return "hello guys"; }}Copy the code
Modifying configuration files
application.properties
Adding Port Information
server.port=8000
Copy the code
POM
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5. RELEASE</version>
<relativePath/> <! -- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1 - the SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Copy the code
The test application
Start the application with localhost:8000/ API /hello
The application is created successfully.
Add Spring Boot Security support
POM
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5. RELEASE</version>
<relativePath/> <! -- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1 - the SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Copy the code
The test application
The following log is generated when the application is started:
Springboot Security creates a user and a request Security chain. The default user name is user password, which is generated randomly and printed in the startup log. In this case, it is abfb4748-61e9-45d8-BC22-72a21d45DF6a
Test application, access interface: localhost:8000/ API /hello
The login page is displayed
Enter the user name and password, and the interface response page is displayed
The user name and password are changed
You can customize the user name/password in the configuration file
application.properties
server.port=8000
spring.security.user.name=levi
spring.security.user.password=123456
Copy the code
MVC Security
The default security configuration in SecurityAutoConfiguration and UserDetailsServiceAutoConfiguration implementation. SecurityAutoConfiguration implementation SpringBootWebSecurityConfigurationWeb security and authentication through UserDetailsServiceAutoConfiguration configurations, This is also important in non-Web applications. To completely turn off the default Web application Security configuration or incorporate multiple Spring Security components (such as OAuth 2 Client and Resource Server), Please add a type of bean WebSecurityConfigurerAdapter (do not disable UserDetailsService configuration or physical security).
To turn off the configuration of UserDetailsService, you can add the UserDetailsService, AuthenticationProvider, or AuthenticationManager of type.
Can add custom to cover WebSecurityConfigurerAdapter access rules. Spring Boot provides a convenient way to override the access rules for executor endpoints and static resources. EndpointRequest can be used to create RequestMatcher based on management. Endpoints. Web. The base – the path attribute. PathRequest can be used by the RequestMatcher to create for resources in common locations.
IO /spring-boot…
Development configuration class
For custom interception configuration
package com.example.demo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
/ * * *@authorLittle hidden Lele *@since2020/11/8 * / shall lie
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/signin").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean(a) throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Override
public UserDetailsService userDetailsService(a) {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return newInMemoryUserDetailsManager(user); }}Copy the code
Develop the login entity class
Used to complete the login entity
package com.example.demo.dto;
import com.sun.istack.internal.NotNull;
/ * * *@authorLittle hidden Lele *@since2020/11/8 20:22 * /
public class SigninDto {
@NotNull
private String username;
@NotNull
private String password;
protected SigninDto(a) {}
public SigninDto(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername(a) {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword(a) {
return password;
}
public void setPassword(String password) {
this.password = password; }}Copy the code
Transform the controller
package com.example.demo.controller;
import com.example.demo.dto.SigninDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/ * * * *@authorLittle hidden Lele *@since2020/11/8 verily * /
@RestController
@RequestMapping("/api")
public class HelloController {
@Autowired
private AuthenticationManager authenticationManager;
@GetMapping("/hello")
public String hello(a) {
return "hello guys";
}
@PostMapping("/signin")
public Authentication signIn(@RequestBody @Valid SigninDto signInDto) {
return authenticationManager.authenticate(newUsernamePasswordAuthenticationToken(signInDto.getUsername(), signInDto.getPassword())); }}Copy the code
The directory structure
This completes adding the endpoint Signin to the custom interceptor chain
The test application
At this point, the interface API/Hello is still not authorized, we added JWT support.
Add the JWT
Depend on the add
<! -- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt --> <dependency> <groupId>io.jsonwebtoken</groupId> < artifactId > JJWT < / artifactId > < version > 0.9.1 < / version > < / dependency >Copy the code
Develop the token generation class
package com.example.demo.jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Base64;
import java.util.Date;
/ * * *@authorLittle hidden Lele *@since2020/11/8 transgress * /
@Component
public class JwtProvider {
private String secretKey;
private long validityInMilliseconds;
@Autowired
public JwtProvider(@Value("${security.jwt.token.secret-key}") String secretKey,
@Value("${security.jwt.token.expiration}") long milliseconds) {
this.secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
this.validityInMilliseconds = milliseconds;
}
public String createToken(String username) {
//Add the username to the payload
Claims claims = Jwts.claims().setSubject(username);
//Build the Token
Date now = new Date();
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(newDate(now.getTime() + validityInMilliseconds)) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }}Copy the code
The Create Token method creates a JWT Token using the JJWT library. The user name is the subject of the payload. It is signed using the key in the properties file, and the validity of the token is specified in the properties file. The resulting token will have the format header.payload-signature. The header contains the type (JSON Web token) and the hash algorithm (HMAC SHA256). The payload is also called the subject (child) that contains the token, the expiration date in the numeric date value (exp), the time when the JWT was published (IAT), the unique JWT identifier (JTI), and application-specific key-value pairs separated by colons. A signature is a hash that uses the header and payload of the key embedded in the application.
Modifying configuration files
security.jwt.token.secret-key=jwt-token-secret-key-for-encryption
security.jwt.token.expiration=1200000
Copy the code
Develop web filter
package com.example.demo.jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Base64;
import java.util.Date;
import java.util.Optional;
import java.util.function.Function;
/ * * *@authorLittle hidden Lele *@since2020/11/8 who fell * /
public class JwtTokenFilter extends GenericFilterBean {
private String secret;
private static final String BEARER = "Bearer";
private UserDetailsService userDetailsService;
public JwtTokenFilter(UserDetailsService userDetailsService, String secret) {
this.userDetailsService = userDetailsService;
this.secret = Base64.getEncoder().encodeToString(secret.getBytes());
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
throws IOException, ServletException {
String headerValue = ((HttpServletRequest) req).getHeader("Authorization");
getBearerToken(headerValue).ifPresent(token -> {
String username = getClaimFromTokenS((String) token, Claims::getSubject);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if(username.equals(userDetails.getUsername()) && ! isJwtExpired((String) token)) { UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(
newWebAuthenticationDetailsSource().buildDetails((HttpServletRequest) req)); SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); }}); filterChain.doFilter(req, res); }private Optional getBearerToken(String headerVal) {
if(headerVal ! =null && headerVal.startsWith(BEARER)) {
return Optional.of(headerVal.replace(BEARER, "").trim());
}
return Optional.empty();
}
private Date getClaimFromToken(String token, Function<Claims, Date> claimsResolver) {
final Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
return claimsResolver.apply(claims);
}
private String getClaimFromTokenS(String token, Function<Claims, String> claimsResolver) {
final Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
return claimsResolver.apply(claims);
}
private boolean isJwtExpired(String token) {
Date expirationDate = getClaimFromToken(token, Claims::getExpiration);
return expirationDate.before(newDate()); }}Copy the code
This is the same as a Web filter in Java EE. This Web filter checks if the token is expired (that is, Jwt is expired). It parses the payload and gets Claims from the token. It strips the bearer string, which is the value of the authorization header. If the token passes all the checks, the Do Filter method configures Spring security to manually set authentication in the context. We specify that the current user is authenticated for successful passage.
Next, add some lines to the Web security configuration class.
WebSecurityConfiguration transformation
package com.example.demo.config;
import com.example.demo.jwt.JwtTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/ * * *@authorLittle hidden Lele *@since2020/11/8 * / shall lie
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Value("${security.jwt.token.secret-key}")
private String secret;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/signin").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(new JwtTokenFilter(userDetailsService, secret), UsernamePasswordAuthenticationFilter.class);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean(a) throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Override
public UserDetailsService userDetailsService(a) {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return newInMemoryUserDetailsManager(user); }}Copy the code
Interface Class Modification
package com.example.demo.controller;
import com.example.demo.dto.SigninDto;
import com.example.demo.jwt.JwtProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/ * * *@authorLittle hidden Lele *@since2020/11/8 verily * /
@RestController
@RequestMapping("/api")
public class HelloController {
@Autowired
private JwtProvider jwtProvider;
@Autowired
private AuthenticationManager authenticationManager;
@GetMapping("/hello")
public String hello(a) {
return "hello guys";
}
@PostMapping("/signin")
public String signIn(@RequestBody @Valid SigninDto signInDto) {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(signInDto.getUsername(), signInDto.getPassword()));
return jwtProvider.createToken(signInDto.getUsername());
} catch (AuthenticationException e) {
System.out.println("Log in failed for user, " + signInDto.getUsername());
}
return ""; }}Copy the code
The test application
Signin interface call to obtain token
The Hello interface call carries a token
conclusion
The Spring Boot Security and JWT Hello World examples are complete. In summary, when login authentication passes, we create a JSON Web token and return it to the caller. The caller then places the JWT in the header and uses the authorization key in its subsequent GET requests. The Web filter checks the validity of the token. If valid, the Web filter will pass it through the filter chain and return “Hello World.”