How to quickly view the IP address and other information of the login user in Spring Security?

Last article I've talked about how to use a more elegant way to customize Spring Security login logic. A more elegant way can effectively avoid the inefficiency brought by custom filters. I suggest you read it and understand the authentication logic in Spring Security by the way.

Based on the above, this paper will continue to discuss with you how to store the login user details.

This is the 12th article in this series. Reading the previous articles in this series can better understand this article:

  1. Dig a big hole and start Spring Security!
  2. Song Ge takes you to Spring Security by hand. Don't ask how to decrypt the password again
  3. How to customize form login in Spring Security
  4. Spring Security does front and back separation, let's not do page Jump! All JSON interactions
  5. The authorization operation in Spring Security is so simple
  6. How does Spring Security store user data in the database?
  7. Spring Security+Spring Data Jpa join hands, security management is only simpler!
  8. Spring Boot + Spring Security for automatic login
  9. How to control security risks when Spring Boot automatically logs in?
  10. In micro service projects, what is the advantage of Spring Security over Shiro?
  11. Two ways to customize the authentication logic of spring security (advanced play method)

Well, no nonsense. Let's look at today's article.

1.Authentication

I've talked to you many times in front of the Authentication interface, and I'd like to talk again today.

The Authentication interface is used to store our login user information. In fact, it further encapsulates the principal (java.security.Principal).

Let's look at a definition of Authentication:

public interface Authentication extends Principal, Serializable {
	Collection<!--? extends GrantedAuthority--> getAuthorities();
	Object getCredentials();
	Object getDetails();
	Object getPrincipal();
	boolean isAuthenticated();
	void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

The interface is explained as follows:

  1. The getAuthorities method is used to get the user's permissions.
  2. The getCredentials method is used to obtain user credentials, which is generally called passwords.
  3. The getDetails method is used to get the details carried by the user, which may be the current request or something like that.
  4. The getPrincipal method is used to get the current user. It may be a user name or a user object.
  5. Is the current user authenticated.

Here is a more fun method called getDetails. For this method, the source code is explained as follows:

Stores additional details about the authentication request. These might be an IP address, certificate serial number etc.

From this explanation, we can see that this method is actually used to store other information about identity authentication, such as IP address, certificate information, etc.

In fact, by default, the IP address and sessionId of the user's login are stored here. Let's look at the source code.

2. Source code analysis

SongGe's spring security series has been written to Article 12. After reading the previous articles, I believe you have understood that a filter that users must pass for login is UsernamePasswordAuthenticationFilter. In the attemptAuthentication method of this class, extract the request parameters. In the attemptAuthentication method, you will call a method, setDetails.

Let's look at the setDetails method:

protected void setDetails(HttpServletRequest request,
		UsernamePasswordAuthenticationToken authRequest) {
	authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}

UsernamePasswordAuthenticationToken is the specific implementation of Authentication, so here is actually setting details. As for the value of details, it is built by authenticationDetailsSource. Let's look at the following:

public class WebAuthenticationDetailsSource implements
		AuthenticationDetailsSource<httpservletrequest, webauthenticationdetails> {
	public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
		return new WebAuthenticationDetails(context);
	}
}
public class WebAuthenticationDetails implements Serializable {
	private final String remoteAddress;
	private final String sessionId;
	public WebAuthenticationDetails(HttpServletRequest request) {
		this.remoteAddress = request.getRemoteAddr();

		HttpSession session = request.getSession(false);
		this.sessionId = (session != null) ? session.getId() : null;
	}
    //Omit other methods
}

By default, WebAuthenticationDetailsSource is used to build WebAuthenticationDetails, and the result is set to the details property of Authentication. As for the properties defined in WebAuthenticationDetails, you can basically see that this is to save the user's login address and sessionId.

So you can basically understand that the IP address of user login can be obtained directly from web authentication details.

Let me give you a simple example. For example, after we log in successfully, we can get the user's IP anytime and anywhere by the following methods:

@Service
public class HelloService {
    public void hello() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        WebAuthenticationDetails details = (WebAuthenticationDetails) authentication.getDetails();
        System.out.println(details);
    }
}

The reason why this acquisition process is done in service is to demonstrate this feature anytime, anywhere. Then we call this method in controller. When we access the interface, we can see the following logs:

WebAuthenticationDetails@fffc7f0c: RemoteIpAddress: 127.0.0.1; SessionId: 303C7F254DF8B86667A2B20AA0667160

As you can see, the IP address and SessionId of the user are given. These two properties have corresponding get methods in WebAuthenticationDetails, and the property values can also be obtained separately.

3. Customization

Of course, WebAuthenticationDetails can also be customized, because it only provides IP and sessionid information by default. If we want to save more information about Http requests, we can customize WebAuthenticationDetails.

If we want to customize web authentication details, we need to redefine it together with web authentication details source.

Combination Last article I'll show you an example of a custom web authentication details.

Last article We use the MyAuthenticationProvider class to determine the verification code. Let's review it Last article Code:

public class MyAuthenticationProvider extends DaoAuthenticationProvider {

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String code = req.getParameter("code");
        String verify_code = (String) req.getSession().getAttribute("verify_code");
        if (code == null || verify_code == null || !code.equals(verify_code)) {
            throw new AuthenticationServiceException("Verification code error");
        }
        super.additionalAuthenticationChecks(userDetails, authentication);
    }
}

However, this verification operation can also be done in the user-defined WebAuthenticationDetails. We define the following two classes:

public class MyWebAuthenticationDetails extends WebAuthenticationDetails {

    private boolean isPassed;

    public MyWebAuthenticationDetails(HttpServletRequest req) {
        super(req);
        String code = req.getParameter("code");
        String verify_code = (String) req.getSession().getAttribute("verify_code");
        if (code != null &amp;&amp; verify_code != null &amp;&amp; code.equals(verify_code)) {
            isPassed = true;
        }
    }

    public boolean isPassed() {
        return isPassed;
    }
}
@Component
public class MyWebAuthenticationDetailsSource implements AuthenticationDetailsSource<httpservletrequest,mywebauthenticationdetails> {
    @Override
    public MyWebAuthenticationDetails buildDetails(HttpServletRequest context) {
        return new MyWebAuthenticationDetails(context);
    }
}

First of all, we define MyWebAuthenticationDetails. Because the HttpServletRequest object is just provided in its construction method, we can directly use this object to judge the verification code and give the result to isPassed variable to save. If we want to extend the properties, we only need to define more properties in MyWebAuthenticationDetails, and then extract them from HttpServletRequest to set them to the corresponding properties. In this way, we can get these properties anytime, anywhere after the login is successful.

Finally, MyWebAuthenticationDetails is constructed in MyWebAuthenticationDetails source and returned.

After the definition, we can call directly in MyAuthenticationProvider:

public class MyAuthenticationProvider extends DaoAuthenticationProvider {

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        if (!((MyWebAuthenticationDetails) authentication.getDetails()).isPassed()) {
            throw new AuthenticationServiceException("Verification code error");
        }
        super.additionalAuthenticationChecks(userDetails, authentication);
    }
}

Get details directly from authentication and call isPassed method. If there is a problem, throw an exception.

The final problem is how to replace the default WebAuthenticationDetailsSource with the customized MyWebAuthenticationDetailsSource. It's very simple. We just need to define it in SecurityConfig:

@Autowired
MyWebAuthenticationDetailsSource myWebAuthenticationDetailsSource;
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            ...
            .and()
            .formLogin()
            .authenticationDetailsSource(myWebAuthenticationDetailsSource)
            ...
}

Inject MyWebAuthenticationDetailsSource into SecurityConfig and configure authenticationDetailsSource in formLogin to successfully use our customized WebAuthenticationDetails.

After the customization is completed, the original functions in WebAuthenticationDetails remain, that is to say, we can use the old methods to continue to obtain user IP and sessionId information, as follows:

@Service
public class HelloService {
    public void hello() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        MyWebAuthenticationDetails details = (MyWebAuthenticationDetails) authentication.getDetails();
        System.out.println(details);
    }
}

In this case, when the type is forced to change to MyWebAuthenticationDetails.

This case can be downloaded from GitHub: https://github.com/lenve/spring-security-samples

OK, I don't know if my friends have got it? If you GET it, please remember to watch and encourage brother song ~ < / HttpServletRequest, mywebauthentication details > < HttpServletRequest, >

Keywords: Programming Spring Session github JSON

Added by jimjack145 on Wed, 06 May 2020 11:21:22 +0300