当前位置:网站首页>Kill session? This cross domain authentication solution is really elegant
Kill session? This cross domain authentication solution is really elegant
2022-06-12 00:40:00 【Silent King II】
User login authentication is Web A very common business in applications , The general process is like this :
- The client sends the user name and password to the server
- After the server-side verification passes , In the current session (session) Save relevant data in , For example, login time 、 Sign in IP etc. .
- The server returns a... To the client session_id, The client saves it in Cookie in .
- When the client sends a request to the server again , take session_id Send it back to the server .
- On the server side session_id after , Identify the user .
In the case of a single machine , There is no problem with this model , But for front and rear end separation Web applications , It's very painful . So there is another solution , The server side will no longer save session data , Instead, save it on the client , Each time the client initiates a request, it sends the data to the server for verification .JWT(JSON Web Token) It is a typical representative of this scheme .

One 、 About JWT
JWT, It's the most popular Cross domain Certification solutions : The client initiates a user login request , After the server receives and authenticates successfully , Generate a JSON object ( As shown below ), And then it's returned to the client .
{
"sub": "wanger",
"created": 1645700436900,
"exp": 1646305236
}
When the client communicates with the server again , Put this JSON Take the object with you , As a certificate of mutual trust between the front and back ends . After the server receives the request , adopt JSON Object to authenticate the user , This eliminates the need to save any session Data. .
If I use a user name now wanger And password 123456 Access programming meow (Codingmore) Of login Interface , So practical JWT It's a string that looks like it's encrypted .

In order to let everyone see more clearly , I copied it to jwt Its official website .

left Encoded Part is JWT Ciphertext , Intermediate use 「.」 Divided into three parts ( On the right side Decoded part ):
- Header( Head ), describe JWT Metadata , among
algThe algorithm of attribute representation signature ( At present, it is HS512); - Payload( load ), It is used to store the data that needs to be transferred , among
subProperty represents the subject ( The actual value is user name ),createdAttribute representation JWT Time of birth ,expProperty indicates the expiration time - Signature( Signature ), Signature of the first two parts , Prevent data tampering ; You need to specify a key on the server side ( Only the server side knows ), Can't leak to client , And then use Header The signature algorithm specified in , Follow the formula below to generate a signature :
HMACSHA512(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
your-256-bit-secret
)
After you figure out the signature , And then Header、Payload、Signature Concatenate into a string , Intermediate use 「.」 Division , You can return it to the client .
The client gets JWT after , Can be placed in localStorage, It can also be placed in Cookie Inside .
const TokenKey = '1D596CD8-8A20-4CEC-98DD-CDC12282D65C' // createUuid()
export function getToken () {
return Cookies.get(TokenKey)
}
export function setToken (token) {
return Cookies.set(TokenKey, token)
}
When the client communicates with the server in the future , Just take this JWT, Generally placed on HTTP The header of the request Authorization In the field .
Authorization: Bearer <token>

After the server receives the request , Right again JWT To verify , If the verification is passed, the corresponding resources will be returned .
Two 、 actual combat JWT
First step , stay pom.xml Add... To the file JWT Dependence .
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
The second step , stay application.yml Add JWT Configuration item for .
jwt:
tokenHeader: Authorization #JWT Stored request header
secret: codingmore-admin-secret #JWT The key used for encryption and decryption
expiration: 604800 #JWT Beyond the deadline of (60*60*24*7)
tokenHead: 'Bearer ' #JWT Get the beginning of the load
The third step , newly build JwtTokenUtil.java Tool class , There are three main ways :
generateToken(UserDetails userDetails): Generated according to the logged in user tokengetUserNameFromToken(String token): from token Get login uservalidateToken(String token, UserDetails userDetails): Judge token Whether it is still valid
public class JwtTokenUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
@Value("${jwt.tokenHead}")
private String tokenHead;
/** * According to the user information token */
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
/** * According to the user name 、 Create time generation JWT Of token */
private String generateToken(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
/** * from token Get login user name */
public String getUserNameFromToken(String token) {
String username = null;
Claims claims = getClaimsFromToken(token);
if (claims != null) {
username = claims.getSubject();
}
return username;
}
/** * from token In order to get JWT Load in */
private Claims getClaimsFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
LOGGER.info("JWT Format validation failed :{}", token);
}
return claims;
}
/** * verification token Is it still valid * * @param token From the client token * @param userDetails User information from the database */
public boolean validateToken(String token, UserDetails userDetails) {
String username = getUserNameFromToken(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
/** * Judge token Is it invalid */
private boolean isTokenExpired(String token) {
Date expiredDate = getExpiredDateFromToken(token);
return expiredDate.before(new Date());
}
/** * from token Get the expiration time in the */
private Date getExpiredDateFromToken(String token) {
Claims claims = getClaimsFromToken(token);
return claims.getExpiration();
}
}
Step four , stay UsersController.java Newly added login Login interface , Receive user name and password , And will JWT Return to the client .
@Controller
@Api(tags=" user ")
@RequestMapping("/users")
public class UsersController {
@Autowired
private IUsersService usersService;
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@ApiOperation(value = " Log in and return to token")
@RequestMapping(value = "/login", method = RequestMethod.POST)
@ResponseBody
public ResultObject login(@Validated UsersLoginParam users, BindingResult result) {
String token = usersService.login(users.getUserLogin(), users.getUserPass());
if (token == null) {
return ResultObject.validateFailed(" Wrong user name or password ");
}
// take JWT Pass back to the client
Map<String, String> tokenMap = new HashMap<>();
tokenMap.put("token", token);
tokenMap.put("tokenHead", tokenHead);
return ResultObject.success(tokenMap);
}
}
Step five , stay UsersServiceImpl.java Newly added login Method , Query the user from the database according to the user name , Generate after password verification JWT.
@Service
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements IUsersService {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private JwtTokenUtil jwtTokenUtil;
public String login(String username, String password) {
String token = null;
// The password needs to be passed after being encrypted by the client
try {
// Query the user + User resources
UserDetails userDetails = loadUserByUsername(username);
// Verify password
if (!passwordEncoder.matches(password, userDetails.getPassword())) {
Asserts.fail(" Incorrect password ");
}
// return JWT
token = jwtTokenUtil.generateToken(userDetails);
} catch (AuthenticationException e) {
LOGGER.warn(" Login exception :{}", e.getMessage());
}
return token;
}
}
Step six , newly added JwtAuthenticationTokenFilter.java, Every time the client makes a request, it will respond to JWT To verify .
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
// Get... From the client request JWT
String authHeader = request.getHeader(this.tokenHeader);
// The JWT It's the format we stipulated , With tokenHead start
if (authHeader != null && authHeader.startsWith(this.tokenHead)) {
// The part after "Bearer "
String authToken = authHeader.substring(this.tokenHead.length());
// from JWT Get user name
String username = jwtTokenUtil.getUserNameFromToken(authToken);
LOGGER.info("checking username:{}", username);
// SecurityContextHolder yes SpringSecurity A tool class of
// Save the security context of the current user in the application
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// Obtain login user information according to user name
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
// verification token Is it overdue
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
// Save the logged in user in a security context
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails,
null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
LOGGER.info("authenticated user:{}", username);
}
}
}
chain.doFilter(request, response);
}
}
JwtAuthenticationTokenFilter Inherited OncePerRequestFilter, This filter ensures that a request passes only once filter, It doesn't need to be repeated . in other words , Every time the client initiates a request , The filter will execute once .
This filter is very critical , Basically, I added comments to every line of code , Yes, of course , To make sure everyone can figure out what this class does , Let me draw another flow chart , So it's clear .

SpringSecurity Is a security management framework , You can talk to Spring Boot Application seamless connection ,SecurityContextHolder Is one of the key tool classes , Hold security context information , Who is the user who saves the current operation , Whether the user has been authenticated , Key information such as permissions owned by users .
SecurityContextHolder Default used ThreadLocal Policies to store authentication information ,ThreadLocal It's characterized by the data in it , Which thread has stored , Which thread can access . This means that after different requests enter the server , There will be different Thread To deal with , For example, threads A The request 1 User information is stored in ThreadLocal, Threads B Processing request 2 You can't get the user's information when you are .
So JwtAuthenticationTokenFilter The filter will do it every time a request comes JWT Validation of the , Ensure that the request from the client is secure . then SpringSecurity The next request interface will be released . This is also JWT and Session Fundamental difference :
- JWT You need to verify every request , And as long as JWT No expired , Even if the server is restarted , The certification is still valid .
- Session Without expiration, it is not necessary to re verify the user information , When the server is restarted , The user needs to log in again to get a new Session.
in other words , stay JWT Under the scheme of , The key saved on the server side (secret) We must not divulge , Otherwise, the client can forge the user's authentication information according to the signature algorithm .
3、 ... and 、Swagger Add JWT verification
For back-end developers , How to be in Swagger( Integrated Knife4j Beautify ) Add JWT What about verification ?
First step , visit login Interface , Enter your user name and password to log in , Get the information returned by the server JWT.

The second step , Collect the data returned by the server tokenHead and token, Fill it in Authorize( Be careful tokenHead and token There is a space between ) Complete login authentication .

The third step , When another interface is requested ,Swagger Will automatically Authorization Send it to the server as request header information .

Step four , After the server receives the request , Will pass JwtAuthenticationTokenFilter Filter right JWT check .

Only this and nothing more , The whole process has been opened up , perfect !
Four 、 summary
To sum up , use JWT To solve the cross domain authentication in the front and rear end separation project is very smooth , This is mainly due to JSON The generality of , Can cross language ,JavaScript and Java All support ; in addition ,JWT The composition of is very simple , Very easy to transmit ; also JWT There is no need to save session information on the server side (Session), Very easy to expand .
Yes, of course , In order to ensure JWT The security of , Not in JWT Save sensitive information in , Because once the private key is compromised ,JWT It is easy to decrypt on the client ; If possible , Please use HTTPS agreement .
Reference link :
Ruan Yifeng :https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
Spring, summer, autumn and winter :https://segmentfault.com/a/1190000012557493
A little rain in Jiangnan :https://cloud.tencent.com/developer/article/1612175
Dearmadman:https://www.jianshu.com/p/576dbf44b2ae
mcarozheng:http://www.macrozheng.com/
Source path :
This article has been included in GitHub On the star 1.6k+ star Open source column 《Java The road to advancement of programmers 》, It is said that every excellent Java Programmers like her , Humor and wit humor 、 Easy to understand . The content includes Java Basics 、Java Concurrent programming 、Java virtual machine 、Java Enterprise development 、Java Interview and other core knowledge points . learn Java, Just recognize it Java The road to advancement of programmers .
https://github.com/itwanger/toBeBetterJavaer
star Having this warehouse means that you have become an excellent Java The potential of Engineers . You can also stamp the link below to jump to 《Java The road to advancement of programmers 》 The official website of , Start a pleasant learning journey .

Nothing makes me stay —— Except for the purpose , Even if there are roses by the shore 、 There's shade 、 There is a peaceful harbor , I'm not a boat .
边栏推荐
- Creating and running JMeter performance test scenarios
- Jiaming's day13 of C learning -- structure and structure pointer
- R language spline curve piecewise linear regression model piecewise regression estimation of individual stock beta value analysis of yield data
- 一、Flutter 入门学习写一简单客户端
- Point cloud library PCL from introduction to mastery learning records Chapter 8
- Lambda中间操作flatMap
- DDD exaggeration, Eric Evans made a bad start
- What is bonded warehouse and what is the difference between them
- 深度学习100例 | 第41天:语音识别 - PyTorch实现
- Lambda中间操作map
猜你喜欢

Is the o2o platform worth doing in 2022

System.CommandLine选项Option

KV storage separation principle and performance evaluation of nebula graph

Flink CDC + Hudi 海量数据入湖在顺丰的实践

Environment construction 2

At the digital data nextionbi online conference, traditional enterprises showed their in-depth understanding of data analysis
![[academic related] it is suggested to collect. Which behaviors are academic misconduct?](/img/a7/2140744ebad9f1dc0a609254cc618e.jpg)
[academic related] it is suggested to collect. Which behaviors are academic misconduct?

Enterprise wechat H5_ Integrated message decryption class, message push get and post callback processing

Experiment 5 constructor and destructor

在玻璃上构建电路
随机推荐
Flutter 使用本地图片
2022 edition of global and Chinese hexamethylene chloride industry dynamic research and investment prospect forecast report
C language preprocessing instructions - learning 21
1、 Getting started with flutter learn to write a simple client
Explore table component virtualization
LabVIEW Arduino电子称重系统(项目篇—1)
Share an open source, free and powerful video player library
Gin integrated graphic verification code
Motianlun domestic database salon | Xu Li: Alibaba cloud's native lindorm TSDB database drives the upgrading of industrial it & ot hyper integrated digital system
Jmeter性能测试场景的创建和运行
Lambda中间操作limit
repeat_ L2-009 red envelope grabbing_ sort
Lambda中间操作skip
[case] building a universal data lake for Fuguo fund based on star ring technology data cloud platform TDC
Characteristics of JS logical operators
Global and Chinese chromatographic silica gel resin industry research and investment direction forecast report 2022 Edition
Breadth first search depth first search dynamic programming leetcode topic: delivering information
Environment construction 2
Point cloud library PCL from introduction to mastery learning records Chapter 8
Lambda中间操作sorted