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

How to carry out Personalized Authentication and RememberMe implementation

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

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

How to carry out personalized authentication and RememberMe implementation, I believe that many inexperienced people do not know what to do, so this paper summarizes the causes of the problem and solutions, through this article I hope you can solve this problem.

Spring Security parsing (3)-- Personalized Authentication and RememberMe implementation

When learning Spring Cloud, when encounter authorization service oauth-related content, always have little knowledge, so decided to first Spring Security, Spring Security Oauth3 and other permissions, authentication-related content, principles and design learning and collation. This series of articles is written to strengthen the impression and understanding in the process of learning. Please let me know if there is any infringement.

Project environment: >-JDK1.8 >-Spring boot 2.x >-Spring Security 5.x

1. Personalized authentication (1) configure login

   we use Security's default login page (/ login) in both the authorization process and the authentication process, so what if we want to customize a login page? Actually, it's very simple. We create a new FormAuthenticationConfig configuration class, and then implement the following settings in the configure (HttpSecurity http) method:

Http.formLogin () / / you can set a custom login page or (login) interface / / Note 1: generally speaking, when it is set to (login) interface, the API will be configured to be accessible without permission, so it will go anonymous filter, which means that it will not go through the authentication process. Therefore, we generally do not directly set it to the interface address / / Note 2: the address configured here must be configured with no permission to access. Otherwise, the problem of constant redirection will occur (because the login page url configured here will be redirected after no permission) .loginPage (securityProperties.getLogin (). GetLoginPage ()) / / .loginPage ("/ loginRequire") / / specify the URL of the authentication credential (default is / login) / / Note 1: the modified url here means that UsernamePasswordAuthenticationFilter will verify the url / / Note 2: it is different from the interface address set by loginPage, but the access interface url is set by loginPage Then the configuration here will not make any sense / / Note 3: the Url set here is a .loginProcessingUrl (securityProperties.getLogin () .loginProcessingUrl ()) / / sets the successful and failed processors respectively. RoomHandler (customAuthenticationSuccessHandler) .failureHandler (customAuthenticationFailureHandler)

Finally,    calls formAuthenticationConfig.configure (http) in the configure (HttpSecurity http) method of SpringSecurityConfig.

   as you can see, we use loginPage () to set the login page or interface, and loginProcessingUrl () to set the interface address that UsernamePasswordAuthenticationFilter wants to match (must be Post) (students who have seen the authorization process should know that its default is / login). Here are the following points to note:

>-loginPage () the address configured here (whether it is the interface url or the login page) must be configured with no permission to access, otherwise the problem of constant redirection will occur (because if there is no permission, it will be redirected to the login page configured here url >-loginPage () is generally not directly set to (login) interface. Because the interface is configured to be accessible without permission (of course, the login page also needs to be configured with no access), anonymous filter will be used, which means that the authentication process will not be followed. Therefore, we generally do not directly set the interface address >-loginProcessingUrl () the modified url here will mean that UsernamePasswordAuthenticationFilter will verify that the url >-loginProcessingUrl () here the Url set here has default no permission access, which is different from the interface address set by loginPage, but loginPage sets the interface url. Then the configuration here will not make any sense >-successHandler () and failureHandler set the authentication success processor and authentication failure processor respectively (if you have no impression of these two processors, it is recommended to review the authorization process)

(II) configuration of successful and failed processors

During the authorization process of   , we briefly mentioned these two processors. The default processors in Security are SavedRequestAwareAuthenticationSuccessHandler and SimpleUrlAuthenticationFailureHandler. This time, we customize these two processors and override the onAuthenticationSuccess () method for CustomAuthenticationSuccessHandler (extends SavedRequestAwareAuthenticationSuccessHandler):

@ Component ("customAuthenticationSuccessHandler") @ Slf4jpublic class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {@ Autowired private SecurityProperties securityProperties; private RequestCache requestCache = new HttpSessionRequestCache (); @ Override public void onAuthenticationSuccess (HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {logger.info ("login successful") / / if loginSuccessUrl is set, always jump to the set address / / if not, try to jump to the address accessed before login. If the pre-login access address is empty, jump to the root path of the website if (! StringUtils.isEmpty (securityProperties.getLogin (). GetLoginSuccessUrl () {requestCache.removeRequest (request, response); setAlwaysUseDefaultTargetUrl (true) SetDefaultTargetUrl (securityProperties.getLogin (). GetLoginSuccessUrl ());} super.onAuthenticationSuccess (request, response, authentication);}}

And CustomAuthenticationFailureHandler (extends SimpleUrlAuthenticationFailureHandler) override the onAuthenticationFailure () method:

@ Component ("customAuthenticationFailureHandler") @ Slf4jpublic class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {@ Autowired private ObjectMapper objectMapper; @ Autowired private SecurityProperties securityProperties; private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy (); @ Override public void onAuthenticationFailure (HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {logger.info ("login failure") If (StringUtils.isEmpty (securityProperties.getLogin (). GetLoginErrorUrl ()) {response.setStatus (HttpStatus.INTERNAL_SERVER_ERROR.value ()); response.setContentType ("application/json;charset=UTF-8"); response.getWriter () .write (objectMapper.writeValueAsString (exception.getMessage () } else {/ / login failure page set by redirectStrategy.sendRedirect (request,response,securityProperties.getLogin (). GetLoginErrorUrl ());}} (3) Custom login page

Instead of describing it here, just paste the code:

Login page username: password: remember me to login

   note that the address requested here is the one configured by loginProcessingUrl ().

(4) Test and verification

   will not post the result map here, as long as we understand that the result flow is as follows: localhost:8080-> Click Test to verify Security permission Control-> Jump to our custom / loginUp.html login page. After logging in-> with configured loginSuccessUrl, jump to loginSuccess.html; or directly jump to / get_user/test API to return the result. The whole process involves our custom login page and custom login success / failure processor.

2. RememberMe (remember me) function parsing (1) RememberMe function implementation configuration first we add the rememberMe configuration, and then take a look at the phenomenon:

1. Create a persistent_logins table to store the associated information between token and users:

Create table persistent_logins (username varchar (64) not null, series varchar (64) primary key, token varchar (64) not null, last_used timestamp not null)

2. Add rememberMe configuration information

@ Bean public PersistentTokenRepository persistentTokenRepository () {JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl (); tokenRepository.setDataSource (dataSource); / / if the token table does not exist, use the following statement to initialize the persistent_logins table (ddl is under the db directory); if so, comment out this statement, otherwise an error will be reported. / / tokenRepository.setCreateTableOnStartup (true); return tokenRepository;} @ Override protected void configure (HttpSecurity http) throws Exception {formAuthenticationConfig.configure (http); http. .... .and () / / enable the remember me function, which means that RememberMeAuthenticationFilter will get token information from Cookie. Remember me () / sets tokenRepository, where jdbcTokenRepositoryImpl is used by default It means that we will read the user information represented by token from the database. TokenRepository (persistentTokenRepository ()) / sets the userDetailsService. Like the authentication process, RememberMe has a special RememberMeAuthenticationProvider, which means that you need to load the UserDetails information using UserDetailsService. UserDetailsService (userDetailsService) / / sets the validity time of the Repository Here, set .tokenValiditySeconds (securityProperties.getLogin (). GetRememberMeSeconds ()) .and () .csrf () .disable () through configuration. / / disable csrf cross-site (domain) attack prevention and control}

Here is an explanation of the configuration:

RememberMe () turns on the remember me function, which means that RememberMeAuthenticationFilter will get token information from Cookie

TokenRepository () configures the acquisition policy for token, which is configured here to read from the database

UserDetailsService () configure UserDetaisService (if you are not familiar with this object, it is recommended to review the authentication process)

TokenValiditySeconds () sets the valid time of rememberMe, which is set here through configuration

Another important configuration is on the login page, where it must be name= "remember-me". RememberMe turns on the remermberMe function by verifying this configuration.

Remember me

The practical result of    should be: enter the login page-> check "remember me"-> after success, check the persistent_logins table and find a piece of data-- > restart the project-- > revisit the page that requires login to access, and find that you can access it without logging in-- > Delete the persistent_logins data, wait for the valid time set by token to expire, and then refresh the page and find that you jump to the landing page.

(2) RembemberMe realizes source code parsing

   first let's look at the internal source code of the successfulAuthentication () method of UsernamePasswordAuthenticationFiler (AbstractAuthenticationProcessingFilter):

Protected void successfulAuthentication (HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {/ 1 set the authenticated Authentication object to SecurityContext SecurityContextHolder.getContext (). SetAuthentication (authResult); / / 2 call RememberMe-related service to process rememberMeServices.loginSuccess (request, response, authResult) / / Fire event if (this.eventPublisher! = null) {eventPublisher.publishEvent (new InteractiveAuthenticationSuccessEvent (authResult, this.getClass ();} / / 3 call successful processor successHandler.onAuthenticationSuccess (request, response, authResult);}

We found a line of code that we focused on this time: rememberMeServices.loginSuccess (request, response, authResult). Check the internal source code of this method:

@ Override public final void loginSuccess (HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {/ / here is determining whether the user checked to remember me if (! rememberMeRequested (request, parameter)) {logger.debug ("Remember-me login not requested."); return } onLoginSuccess (request, response, successfulAuthentication);}

Use rememberMeRequested () to determine whether it is checked or not. Remember me. The onLoginSuccess () method will eventually call the onLoginSuccess () method of PersistentTokenBasedRememberMeServices, and the source code of the method is as follows:

Protected void onLoginSuccess (HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {/ / 1 get the account name String username = successfulAuthentication.getName (); / / 2 create the PersistentRememberMeToken object PersistentRememberMeToken persistentToken = new PersistentRememberMeToken (username, generateSeriesData (), generateTokenData (), new Date ()) Try {/ / 3 stores persistentRememberMeToken information tokenRepository.createNewToken (persistentToken) through tokenRepository; / / 4 adds persistentRememberMeToken information to Cookie addCookie (persistentToken, request, response) } catch (Exception e) {logger.error ("Failed to save persistent token", e);}}

Analyze the source code steps:

Get account information username

Input username to create PersistentRememberMeToken object

Store persistentRememberMeToken information through tokenRepository

Add persistentRememberMeToken information to Cookie

   the tokenRepository here is what we set up to configure the rememberMe function. After parsing above, we see that rememberServices will create a token information, store it in the database (because we are configuring the database storage method JdbcTokenRepositoryImpl), and add the token information to the Cookie. At this point, we have seen some business processing before the implementation of RememberMe, so I think we all have a bottom line on how to implement RememberMe later. Here we directly throw out the filter class RememberMeAuthenticationFilter that we did not mention in the previous authorization process, which is a filter between UsernamePasswordAuthenticationFilter and AnonymousAuthenticationFilter. It is mainly responsible for obtaining token information from Cookie after successful authentication and then obtaining login user name through tokenRepository, then UserDetailsServcie loads UserDetails information, and finally creates Authticaton (RememberMeAuthenticationToken) information and calls AuthenticationManager.authenticate () to carry out the authentication process.

RememberMeAuthenticationFilter

  , let's take a look at the dofiler method source code of RememberMeAuthenticationFilter:

Public void doFilter (ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res If (SecurityContextHolder.getContext (). GetAuthentication () = = null) {/ / 1 call rememberMeServices.autoLogin () to get Authtication information Authentication rememberMeAuth = rememberMeServices.autoLogin (request, response) If (rememberMeAuth! = null) {/ / Attempt authenticaton via AuthenticationManager try {/ / 2 calls authenticationManager.authenticate () authentication rememberMeAuth = authenticationManager.authenticate (rememberMeAuth) . }} catch (AuthenticationException authenticationException) {. } chain.doFilter (request, response);}

We mainly focus on the implementation of the rememberMeServices.autoLogin (request,response) method and look at the source code:

@ Override public final Authentication autoLogin (HttpServletRequest request, HttpServletResponse response) {/ / 1 get token information from Cookie String rememberMeCookie = extractRememberMeCookie (request); if (rememberMeCookie = = null) {return null } if (rememberMeCookie.length () = = 0) {cancelCookie (request, response); return null;} UserDetails user = null Try {/ / 2 parses token information String [] cookieTokens = decodeCookie (rememberMeCookie); / / 3 generates Uerdetails information through token information user = processAutoLoginCookie (cookieTokens, request, response); userDetailsChecker.check (user) Logger.debug ("Remember-me cookie accepted"); / / 4 create Authentication return createSuccessfulAuthentication (request, user) through UserDetails information;}. }

Internal implementation steps:

Get token information from Cookie and parse

Generate UserDetails through parsed token (processAutoLoginCookie () method)

Generate Authentication through UserDetails (createSuccessfulAuthentication () create RememberMeAuthenticationToken)

The most important part of this is how the processAutoLoginCookie () method generates UserDetails objects. Let's look at the source code implementation of this method:

Protected UserDetails processAutoLoginCookie (String [] cookieTokens, HttpServletRequest request, HttpServletResponse response) {final String presentedSeries = cookieTokens [0]; final String presentedToken = cookieTokens [1]; / / 1 load database token information PersistentRememberMeToken token = tokenRepository .getTokenForSeries (presentedSeries) through tokenRepository PersistentRememberMeToken newToken = new PersistentRememberMeToken (token.getUsername (), token.getSeries (), generateTokenData (), new Date ()); / / 2 to determine whether the token passed by the user is consistent with the token in the data. Inconsistency may have security problems if (! presentedToken.equals (token.getTokenValue () {tokenRepository.removeUserTokens (token.getUsername () Throw new CookieTheftException (messages.getMessage ("PersistentTokenBasedRememberMeServices.cookieStolen", "Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack. ");} try {/ / 3 updates token and adds tokenRepository.updateToken (newToken.getSeries (), newToken.getTokenValue (), newToken.getDate ()) to Cookie; addCookie (newToken, request, response) } catch (Exception e) {throw new RememberMeAuthenticationException ("Autologin failed due to data access problem");} / 4 loads UserDetails information through the UserDetailsService () .loadUserByUsername () method and returns return getUserDetailsService () .loadUserByUsername (token.getUsername ()) }

Let's take a look at the internal steps:

Load database token information through tokenRepository

Determine whether the token passed by the user is consistent with the token in the data. There may be security problems in the inconsistency.

Update token and add it to Cookie

Load the UserDetails information through the UserDetailsService () .loadUserByUsername () method and return

When    sees this, I believe you will understand why you configured tokenRepository and UserDetailsService when you enabled the rememberMe feature.

Here I will not demonstrate the entire implementation process, the old rules, on the flowchart:

After reading the above, have you mastered how to carry out personalized authentication and how to implement RememberMe? If you want to learn more skills or want to know more about it, you are welcome to follow the industry information channel, thank you for reading!

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

Internet Technology

Wechat

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

12
Report