Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Example Analysis of Spring Security Authentication

2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article will explain in detail the example analysis of Spring Security authentication for you. The editor thinks it is very practical, so I share it for you as a reference. I hope you can get something after reading this article.

Introduction

I believe that people love and hate the framework of Spring Security, love its strength, hate its tedious, in fact, this is a misunderstanding, Spring Security is indeed very cumbersome, tedious to people. There is no way to hate it, but as JavaEE engineers still have to face it, before we begin, let's make an analogy (for example, poor thing):

Spring Security is like an administrative service center. If we work in it, what can we do? It can be as small as consulting simple questions, inquiring about social security information, household registration, replacement of identity cards, as well as enterprise matters and various complex qualifications. But we don't need to go to the administrative service center and do all the business one by one. In reality, there is no such person.

What do you mean, choose the services (functions) you need, ignore those you don't need, and learn about them when you need them. This is also a suggestion for many engineers, especially the extremely large Java system, do not easily proficient, play all the source code and so on, really meaningless, my brain storage is relatively small, life is short, unnecessary.

Back to business! This article will be unfolded in an easy way, not heap code.

About identity authentication

Web authentication is an area that backend engineers can never avoid. Identity authentication Authentication is different from authorized Authorization. Authentication refers to the authentication of a user's identity, which does not interfere with what the user can or cannot do, but only confirms the existence of the user. And Authorization authorization is established on the basis of authentication, the existence of this user, and then agree that this user can do one thing, which we should distinguish. This article tells the story of Authentication and does not focus on permissions.

To warm up, let's review the evolution of identity authentication:

First is the most famous entry message board program, I believe that many back-end engineers have done message board, that is a basic no framework stage, recall how to authenticate. The form enters the user name and password Submit, and then the back end takes it to the data database to query, and if it can not be found, it mercilessly throws an exception, oh, the password is wrong; found, happily encrypt the user ID and related information into the Session logo and store it, the response is written to Cookie, and subsequent requests are decrypted and verified, right? Yes, body authentication can be as simple as just matching the Session logo. The frustrating thing is that the development of the modern Internet has long passed the era of Web2.0, and the emergence of the client makes identity authentication more complicated. Let's go on.

With the rise of mobile, Android and ios dominate, as well as when users log in to authenticate, get user information, and are ready to follow clues to write back to Session and write back Cookie, and so on! What? Android does not support Cookie? That sounds unscientific, isn't it? it's a little anti-human, isn't it? it's a little overwhelmed.

Hey hey, smart people may have an idea, well, don't Android clients have local storage? Can't you just save the returned data? Sorry again, Android local storage is not as user-friendly as browser Cookie and does not automatically expire. Nothing, and then indicate the expiration time, each time to read the judgment on the line, it seems to be OK.

Wait. The client's Api interface requires lightweight, and one day a teammate wants to achieve something personalized, and unexpectedly sends a string back to Cookie, which seems very convenient, uh-huh. So other teammates followed suit, and Cookie became more complicated. At this time, Android teammates roared, "that's enough!" STOP! All I need is a certification logo, which is simple enough, you know? And when Cookie expires, you have to log in again, the user experience is very poor, the product manager has talked to me dozens of times, the users are running out, and you are still adding some strange things to Cookie.

Oauth 2.0 is coming.

If there is a problem, we must find a way to solve it. The client is not a browser and has its own unique interaction convention, but Cookie has given up. Here are five problems to be solved:

[] only a simple string identification is needed, and there is no need to follow the rules of Cookie

[] the server side needs to be able to easily authenticate this logo, preferably standardized

[] Don't let the user repeatedly enter the password to log in, it can be refreshed automatically.

[] the secret key should be secure, from the network transmission link layer to the client layer, and it can be invalidated even if it is captured midway.

[] clients of multiple subsystems need independent authentication identifiers so that they can exist independently (for example, the authentication status of Taobao will not affect the login authentication status of Ali Wangwang)

Once the demand is determined and the plan is about to come out, let's think about it briefly.

[X] the first is identification, which is the simplest. The user identity data is reversibly encrypted. OK, this is done.

[X] then there is the standardization of identity authentication, preferably lightweight, and let her not interfere with the performance of the request, such as Get and Post data, smart you think, right, that is, Header, let's temporarily unify Userkey as the Header name, and the value is the encrypted identity, concise and rough enough, the backend intercepts and handles every request, if it can be decrypted successfully and is valid. Just tell the little friend in the line behind that this guy is one of his own, named xxx, and has 100 yuan in his pocket. This is done, too.

[X] automatically refresh, because the encrypted ID has to be transmitted every time and cannot be put together, and their functions are also different, so issue a refreshed key when issuing the encrypted ID, which is equivalent to giving you a key card when you enter the job. This card needs to be carried with you, and it is necessary to open the door to sign it. In addition, there is an ID card that you do not need to take with you. Put it at home, the access card is off, it doesn't matter, take the certificate to the security brother there to get another access card, prove that it is effective, when the security brother intimate to give you a certificate.

[X] Security issues, encryption can strengthen part of the security. Do you still need to talk about the transmission link? Encrypt the Https transmission. As for client-side local security is a philosophical issue, uh-huh. Ha ha. For the time being, we think that local private space storage is secure. As the saying goes, computers have been cracked, and we also talk about chicken feather security (so we'd better not go to ROOT phones. Can private storage be accessed after ROOT?)

[X] the problem of subsystem independence is easy to deal with. Another factor is added to the identity authentication process, which is temporarily called Client. In this way, the logo does not affect each other.

After finishing the work, we should begin to implement this system. Don't worry, don't you feel deja vu? Yes, it is the password Grant mode of Oauth 2.0!

How is Spring Security certified?

Let's start with a familiar piece of code:

Http.formLogin () .loginPage ("/ auth/login") .permitAll () .failureHandler (loginFailureHandler) .logHandler (loginSuccessHandler)

Spring Security is like a shy girl, who knows how he is certified, and encapsulates it a little too much. Take a look at a picture first:

A simplification is made here.

According to the process of JavaEE, the essence is the process that Filter filters requests, forwards them to different processing modules for processing, and finally returns Response after business logic processing.

When the request matches the Security Filter defined by us, it will be directed to the Security module for processing, such as UsernamePasswordAuthenticationFilter, and the source code will be presented:

Public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username"; public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password"; private String usernameParameter = "username"; private String passwordParameter = "password"; private boolean postOnly = true; public UsernamePasswordAuthenticationFilter () {super (new AntPathRequestMatcher ("/ login", "POST")) } public Authentication attemptAuthentication (HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {if (this.postOnly & &! request.getMethod (). Equals ("POST")) {throw new AuthenticationServiceException ("Authentication method not supported:" + request.getMethod ());} else {String username = this.obtainUsername (request); String password = this.obtainPassword (request); if (username = null) {username = "" } if (password = = null) {password = ";} username = username.trim (); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken (username, password); this.setDetails (request, authRequest); return this.getAuthenticationManager (). Authenticate (authRequest);}} protected String obtainPassword (HttpServletRequest request) {return request.getParameter (this.passwordParameter);} protected String obtainUsername (HttpServletRequest request) {return request.getParameter (this.usernameParameter) } protected void setDetails (HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {authRequest.setDetails (this.authenticationDetailsSource.buildDetails (request));} public void setUsernameParameter (String usernameParameter) {Assert.hasText (usernameParameter, "Username parameter must not be empty or null"); this.usernameParameter = usernameParameter;} public void setPasswordParameter (String passwordParameter) {Assert.hasText (passwordParameter, "Password parameter must not be empty or null"); this.passwordParameter = passwordParameter } public void setPostOnly (boolean postOnly) {this.postOnly = postOnly;} public final String getUsernameParameter () {return this.usernameParameter;} public final String getPasswordParameter () {return this.passwordParameter;}}

It's a little complicated, isn't it? don't worry, I'll do some pseudo-code to make it look friendlier and easier to understand. Pay attention to the single-line comments I wrote

Public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username"; public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password"; private String usernameParameter = "username"; private String passwordParameter = "password"; private boolean postOnly = true; public UsernamePasswordAuthenticationFilter () {/ / 1. Match URL and Method super (new AntPathRequestMatcher ("/ login", "POST");} public Authentication attemptAuthentication (HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {if (this.postOnly & &! request.getMethod (). Equals ("POST")) {/ / what? You did not use the POST method, give you an exception, reflect on yourself to throw new AuthenticationServiceException ("Authentication method not supported:" + request.getMethod ());} else {/ / get the parameter String username = this.obtainUsername (request) from the request; String password = this.obtainPassword (request) / / I don't know if the username and password are correct, so construct an unauthenticated Token first UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken (username, password); / / by the way, save the request and Token this.setDetails (request, token); / / who should Token handle it? For the current AuthenticationManager, of course. Return this.getAuthenticationManager (). Authenticate (token);}

Is it very clear that the question comes again, what the heck is Token? Why is there a difference between certified and uncertified? Don't worry, let's find out what Token looks like. Go to UsernamePasswordAuthenticationToken:

Public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {private static final long serialVersionUID = 510L; private final Object principal; private Object credentials; public UsernamePasswordAuthenticationToken (Object principal, Object credentials) {super ((Collection) null); this.principal = principal; this.credentials = credentials; this.setAuthenticated (false);} public UsernamePasswordAuthenticationToken (Object principal, Object credentials, Collection var1);}

This is an interface, I like the interface, concise and clear. There is a supports method, return a Boolean value, the parameter is a Class, yes, here is based on the class of Token to determine what Provider to deal with, do you remember the previous code?

/ / who will Token handle it for? For the current AuthenticationManager, of course. Return this.getAuthenticationManager (). Authenticate (token)

So let's move on to the next step, DaoAuthenticationProvider, which inherits AbstractUserDetailsAuthenticationProvider. Congratulations on your persistence and it will be dawn soon. This is more complicated. In order not to let you run away, I will merge two complex classes, extract the logic that directly touches the core of the interface, and add code directly, which will be deleted, so that you can see more clearly and pay attention to the comments:

Public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {/ / familiar supports requires UsernamePasswordAuthenticationToken public boolean supports (Class authentication) {return UsernamePasswordAuthenticationToken.class.isAssignableFrom (authentication);} public Authentication authenticate (Authentication authentication) throws AuthenticationException {/ / take out the value stored in Token String username = authentication.getPrincipal () = null? "NONE_PROVIDED": authentication.getName (); boolean cacheWasUsed = true; / / slave cache UserDetails user = this.userCache.getUserFromCache (username); if (user = = null) {cacheWasUsed = false; / / what, no cache? Use retrieveUser method to get user = this.retrieveUser (username, (UsernamePasswordAuthenticationToken) authentication);} / /. A large part has been deleted so that Object principalToReturn = user; if (this.forcePrincipalAsString) {principalToReturn = user.getUsername ();} return this.createSuccessAuthentication (principalToReturn, authentication, user);} protected final UserDetails retrieveUser (String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {try {/ / familiar loadUserByUsername UserDetails loadedUser = this.getUserDetailsService (). LoadUserByUsername (username) If (loadedUser = = null) {throw new InternalAuthenticationServiceException ("UserDetailsService returned null, which is an interface contract violation");} else {return loadedUser;}} catch (UsernameNotFoundException var4) {this.mitigateAgainstTimingAttack (authentication); throw var4;} catch (InternalAuthenticationServiceException var5) {throw var5;} catch (Exception var6) {throw new InternalAuthenticationServiceException (var6.getMessage (), var6) }} / / verify password protected void additionalAuthenticationChecks (UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {if (authentication.getCredentials () = = null) {this.logger.debug ("Authentication failed: no credentials provided"); throw new BadCredentialsException (this.messages.getMessage ("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials");} else {String presentedPassword = authentication.getCredentials (). ToString () If (! this.passwordEncoder.matches (presentedPassword, userDetails.getPassword ()) {this.logger.debug ("Authentication failed: password does not match stored value"); throw new BadCredentialsException (this.messages.getMessage ("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")) This is the end of the article on "sample Analysis of Spring Security Authentication". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, please share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report