编程知识 cdmana.com

Spring security pit entry (V)

How to customize implementation validation ?

be based on SpringSecurity Do basic permission verification , It was almost written before , By the way, when logging in , Verification of dynamic verification code , These are all in SpringSecuity On a good basis , How to customize the implementation of these logins , Take a closer look , Whether it's based on Memory verification jdbc verification ...

They all need to be configured configure(AuthenticationManagerBuilder auth) Authentication manager generator , Verification method

/**
 *  According to the incoming custom {@link AuthenticationProvider}  Certification provider   Add Authentication .
 *  because {@link AuthenticationProvider} The implementation is unknown , Therefore, all custom operations must be completed externally ,
 *  also {@link AuthenticationManagerBuilder}  Will return immediately .
 */
auth.authenticationProvider(AuthenticationProvider authenticationProvider)

AuthenticationProvider It's an interface , Description inherited AuthenticationProvider Can realize the method of user-defined Authentication ,SpringSecurity Official Document No 9.2.1 Pointed out that AuthenticationProvider,Spring Security The simplest way to implement is DaoAuthenticationProvider So we can implement this method

MyAuthenticationProvider

package com.shaojie.authority.security;

import com.shaojie.authority.component.MyWebAuthenticationDetails;
import com.shaojie.authority.exception.VerificationCodeException;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
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;

/**
 * @author: ShaoJie
 * @data: 2020 year 02 month 10 Japan  20:26
 * @Description:  Custom authentication process 
 * <p>
 *  because  DaoAuthenticationProvider  Also inherited  AbstractUserDetailsAuthenticationProvider
 *  So here we only inherit  DaoAuthenticationProvider
 */
public class MyAuthenticationProvider extends DaoAuthenticationProvider {

    /**
     *  User authentication provider 
     */
    @Autowired
    private UserDetailsService userDetailsService;
    
    /**
     *  Password encryption 
     */
    @Autowired
    public PasswordEncoder passwordEncoder;

    public MyAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        this.setUserDetailsService(userDetailsService);
        this.setPasswordEncoder(passwordEncoder);
    }

    /**
     *  Allow subclasses to return for a given authentication request ( Or cached )UserDetails  Perform any other checks .
     *  Usually , Subclasses will at least  Authentication#getCredentials() And  UserDetails#getPassword() Compare .
     *  If you need custom logic to compare  UserDetails  And or 
     * UsernamePasswordAuthenticationToken  Other * attribute , Then these attributes should also appear in this method .
     *
     * @param userDetails     User information 
     * @param authentication  authentication 
     */
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails,
                                                  UsernamePasswordAuthenticationToken authentication) {
                                                  
         // getCredentials()  Belong to  authentication  Usually used to obtain the credentials of the principal   Usually the user's password 
        //  The custom password verification here is  MyAuthenticationProvider  Inherit   AbstractUserDetailsAuthenticationProvider
        if (authentication.getCredentials() == null) {
            throw new BadCredentialsException(
                    this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials",
                            " The password cannot be empty "));
        } else {
            String password = authentication.getCredentials().toString();
            if (!password.equals(userDetails.getPassword())) {
                this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials",
                        " Wrong password ");
            }
        }
        //  Use the method of the parent class to verify the user 
        super.retrieveUser(userDetails.getUsername(), authentication);
    }
    
}

Here's to this UsernamePasswordAuthenticationToken Should be more confused , Check the source code and find that this class is Authentication Implementation of authentication mode , The authentication method includes the acquisition of information

    /**
	 * The credentials that prove the principal is correct. This is usually a password,
	 * but could be anything relevant to the <code>AuthenticationManager</code>. Callers
	 * are expected to populate the credentials.
	 *
	 * @return the credentials that prove the identity of the <code>Principal</code>
	 */
	Object getCredentials();

	/**
	 * Stores additional details about the authentication request. These might be an IP
	 * address, certificate serial number etc.
	 *
	 * @return additional details about the authentication request, or <code>null</code>
	 * if not used
	 */
	Object getDetails();

A simple translation is Object getCredentials() Method to obtain the authentication subject information of the account , Generally speaking, it is the main information such as password ,Object getDetails() Method stores additional details about the authentication request . But how to verify the verification code ?SpringSecurity Official Document No 10.16.1 Indicates that the pre authentication filter has a authenticationDetailsSource attribute , By default , It will create a WebAuthenticationDetails Object to store other information , That means it needs to be modified SpringSecurity Implement custom authentication configuration

    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().authenticationDetailsSource(new WebAuthenticationDetailsSource());
    }

authenticationDetailsSource Source code :

    /**
	 * Specifies a custom {@link AuthenticationDetailsSource}. The default is
	 * {@link WebAuthenticationDetailsSource}. 
	 *  Specify a custom {@link AuthenticationDetailsSource}  The default is  {@link WebAuthenticationDetailsSource}. 
	 *
	 * @param authenticationDetailsSource the custom {@link AuthenticationDetailsSource}
	 * @return the {@link FormLoginConfigurer} for additional customization
	 */
	public final T authenticationDetailsSource(
			AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
		this.authenticationDetailsSource = authenticationDetailsSource;
		return getSelf();
	}

authenticationDetailsSource Property requires AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource Parameters , But now we need a WebAuthenticationDetails Object to store other information

MyWebAuthenticationDetails Implement verification

package com.shaojie.authority.component;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.web.authentication.WebAuthenticationDetails;

import javax.servlet.http.HttpServletRequest;

/**
 * @author: ShaoJie
 * @data: 2020 year 02 month 10 Japan  21:54
 * @Description:
 */
@Slf4j
public class MyWebAuthenticationDetails extends WebAuthenticationDetails {

    /**
     *  Verify that the code is correct 
     */
    private boolean imageCodeIsRight;

    public boolean getImageCodeIsRight() {
        return this.imageCodeIsRight;
    }

    /**
     * Records the remote address and will also set the session Id if a session already
     * exists (it won't create one).  Save session 
     *  Supplement the verification code and submitted by the user  session  Saved verification code 
     *
     * @param request that the authentication request was received from
     */
    public MyWebAuthenticationDetails(HttpServletRequest request) {
        super(request);
        //  Then verify the value of the form  -->  Graphic verification code 
        String captcha = request.getParameter("captcha");
        log.info(" Verification code of the form captcha: {}", captcha);
        //  Take out   During the interview   Has been added to  session  Verification code in 
        String sessionCaptcha = (String) request.getSession().getAttribute("captcha");
        log.info("session The verification code : {}", sessionCaptcha);
        //  Judge whether the two values are the same 
        if (!StrUtil.isEmpty(sessionCaptcha)) {
            //  Know the current verification code   Whether you succeed or fail   The client login fails. The current verification code should be refreshed 
            request.getSession().removeAttribute("captcha");
            //  When the verification code is correct   Modify the current status 
            if (!StrUtil.isEmpty(captcha) && captcha.equals(sessionCaptcha)) {
                this.imageCodeIsRight = true;
            }
        }
    }
}

MyWebAuthenticationDetailsSource

package com.shaojie.authority.component;

import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * @author: ShaoJie
 * @data: 2020 year 02 month 10 Japan  21:56
 * @Description:
 */
@Component
public class MyWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {

    /**
     *  Called by the class when the class wants to create a new instance of authentication details .
     *
     * @param context  Request object , Can be used by authentication details 
     * @return
     */
    @Override
    public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
        return new MyWebAuthenticationDetails(context);
    }
}

MyWebAuthenticationDetailsSource take HttpServletRequest Pass to MyWebAuthenticationDetails, Used to obtain the information submitted by the user , And verify . At this time, the verification code can be added to the user-defined verification

The modified MyAuthenticationProvider

package com.shaojie.authority.security;

import com.shaojie.authority.component.MyWebAuthenticationDetails;
import com.shaojie.authority.exception.VerificationCodeException;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
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;

/**
 * @author: ShaoJie
 * @data: 2020 year 02 month 10 Japan  20:26
 * @Description:  Custom authentication process 
 * <p>
 *  because  DaoAuthenticationProvider  Also inherited  AbstractUserDetailsAuthenticationProvider
 *  So here we only inherit  DaoAuthenticationProvider
 */
public class MyAuthenticationProvider extends DaoAuthenticationProvider {

    /**
     *  User authentication provider 
     */
    @Autowired
    private UserDetailsService userDetailsService;

    /**
     *  Password encryption 
     */
    @Autowired
    public PasswordEncoder passwordEncoder;

    public MyAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        this.setUserDetailsService(userDetailsService);
        this.setPasswordEncoder(passwordEncoder);
    }

    /**
     *  Allow subclasses to return for a given authentication request ( Or cached )UserDetails  Perform any other checks .
     *  Usually , Subclasses will at least  Authentication#getCredentials() And  UserDetails#getPassword() Compare .
     *  If you need custom logic to compare  UserDetails  And or 
     * UsernamePasswordAuthenticationToken  Other * attribute , Then these attributes should also appear in this method .
     *
     * @param userDetails     User information 
     * @param authentication  authentication 
     * @throws AuthenticationException SneakyThrow  Will avoid javac Insist that you catch or throw forward all check exceptions generated by statements in the method body .
     */
    @SneakyThrows
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails,
                                                  UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        // getCredentials()  Belong to  authentication  Usually used to obtain the credentials of the principal   Usually the user's password 
        //  The custom password verification here is  MyAuthenticationProvider  Inherit   AbstractUserDetailsAuthenticationProvider
        if (authentication.getCredentials() == null) {
            throw new BadCredentialsException(
                    this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials",
                            " The password cannot be empty "));
        } else {
            String password = authentication.getCredentials().toString();
            if (!password.equals(userDetails.getPassword())) {
                this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials",
                        " Wrong password ");
            }
        }

        //  When the inherited class is modified   Now realize the design of graphic verification code   Customize 
        //  Realize the logic of picture verification code 
        MyWebAuthenticationDetails details = (MyWebAuthenticationDetails) authentication.getDetails();
        //  verification   The verification code is correct 
        if (!details.getImageCodeIsRight()) {
            throw new VerificationCodeException();
        }
    }

}

It's not over yet , It needs to be revised SpringSecurity Configuration of , The main configuration is authenticationDetailsSource attribute , take MyWebAuthenticationDetailsSource Inject in , And call to implement custom authentication

    @Autowired
    private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> myWebAuthenticationDetailsSource;
    
    /**
     *  verification 
     *
     * @param http
     * @throws Exception
     */
    //  Instead of the configuration file  <security:http></security:http>
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //  Add permissions 
        selectPurview(http);

        http.authorizeRequests()
                // antMatchers  Set blocked requests   hasAnyAuthority  Corresponding permission name 
                // .hasAnyAuthority("PRODUCT_ADD")  The permissions of the user 
                //  Can be replaced by  .hasRole()  Verify the role 
//                .antMatchers("/product/add").hasAnyAuthority("PRODUCT_ADD")
//                .antMatchers("/product/update").hasAnyAuthority("PRODUCT_UPDATE")
//                .antMatchers("/product/list").hasAnyAuthority("PRODUCT_LIST")
//                .antMatchers("/product/delete").hasAnyAuthority("PRODUCT_DELETE")
                // permitAll  All permissions can access 
                .antMatchers("/login").permitAll()
                .antMatchers("/captcha.jpg").permitAll()
//                .antMatchers("/**")
                // fullyAuthenticated  Anonymous users are not allowed to view 
//                .fullyAuthenticated()
                //  Set that all requests must be authenticated to access 
                .anyRequest().authenticated()
                .and()
                // httpbasic  Sign in 
                // .httpBasic();
                //  Form login 
                .formLogin()
                //   Log in to the requested page 
                .loginPage("/login")
                //  Handling login requests   Address 
                .loginProcessingUrl("/index")
                .authenticationDetailsSource(myWebAuthenticationDetailsSource)
                //  Definition   Faulty processor 
//                 .failureHandler()
                //  modify  spring  Provided   Default login parameters 
                .usernameParameter("userName")
                .passwordParameter("password")
                .and()
                //  Turn on remember me 
                .rememberMe()
                .and()
                //  Enable logout 
                .logout()
                //  Maximum number of sessions 
                .and()
                //  Add filter   take   Filters are added to  UsernamePasswordAuthenticationFilter  Before   That is, before verifying the account password 
                //  Custom implementation   User login blocking 
//                .addFilterBefore(new VerificationCodeFilter(),
//                        UsernamePasswordAuthenticationFilter.class)
                .and()
                .csrf()
        //  Disable cross domain protection 
                .disable();
    }

Custom implementation login here , I found that although the document is stupid , But it seems that compared with the source code , There's still something , Basically everything you need can be found . Novices are not advised to read directly , Some parts of the source code are true, but I can't understand , A lot of places , Look at the words without comparison , Maybe I'm confused , Look at the source code more reliable , Recently, I like to study , I don't catch a cold with a lot of Technology , I don't think I can see much , This year's target Read a book It's going step by step , Come on , Some places may be rough , Take a closer look , I have this dish too .

I won't say much in some places , Before integration , If you have any questions, you can check my GitHub.

Participation of this paper Tencent cloud media sharing plan , You are welcome to join us , share .

版权声明
本文为[Shao Jie]所创,转载请带上原文链接,感谢
https://cdmana.com/2021/08/20210809183600841i.html

Scroll to Top