“This is the second day of my participation in the First Challenge 2022, for more details: First Challenge 2022”.

preface

We usually need login authentication when developing a project. The commonly used login authentication technology implementation frameworks are Spring Security and Shiro

Spring Security

Spring Security is a powerful and highly customizable authentication and access control framework. It is the de facto standard for securing Spring-based applications.

Spring Security is a framework focused on providing authentication and authorization for Java applications. As with all Spring projects, the real power of Spring Security is that it can be easily extended to meet custom requirements and is a much better fit with Spring, which we use a lot in our work.

Apache Shiro

Apache Shiro is a security framework for Java. Apache Shiro is being used by more and more people because it is fairly simple. It may not be as powerful as Spring Security, but it may not need to be that complicated in practice. So using Shiro, which is small and simple, is enough.

Inadequate:

These are all authentication frameworks that are commonly used in a single application, but in a distributed application, where multiple applications may be deployed and requests are distributed through Nginx, each single application may be double-validated because their Seesion data is stored in their own service.

The Session

Session is a communication Session tracking technology between the client and the server. The server and the client maintain the basic information of the entire communication Session.

When the client accesses the server for the first time, the server responds with a sessionId and stores it in a local cookie. In subsequent accesses, the sessionId in the cookie is placed in the request header to access the server.

spring-session

Spring Session is one of the Spring projects that focuses on Session management by replacing httpSession implemented by the servlet container with Spring-Session.

Spring Session provides the Clustered Sessions (Sessions) function. By default, external Redis is used to store Session data to solve the problem of Session sharing.

Spring-session provides a series of apis and implementations for user session management. Provides a number of extensible, transparent encapsulation methods for managing httpSession/WebSocket processing.

Support functions

  1. It is easy to store sessions in third-party storage containers. The framework provides many ways to store sessions in containers such as Redis, JVM Maps, Mongo, Gemfire, Hazelcast, JDBC, and so on. This provides high-quality clustering in an application server-independent manner.
  2. The same browser and the same website support multiple session problems. This makes it easy to build a richer end-user experience.
  3. Restful APIS that do not rely on cookies. The jessionID can be passed through the header. Control how session ids are exchanged between clients and servers so that it’s easy to write Restful apis that get session ids from HTTP headers instead of relying on cookies
  4. WebSocket and Spring-Session are combined to synchronize lifecycle management. When a user sends a request using WebSocket

Distributed Seesion combat

Step 1: Dependency packages

Because it’s a Web application. We added the common dependency package Web of Springboot and the dependency package of SpringSession and Redis to support the storage of session in Redis

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-redis</artifactId>
    <version>1.4.7. RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
Copy the code

Because seesion is stored in Redis, each service login is to check the data in Redis for verification, all are distributed. Spring-session-data-redis and spring-boot-starter-redis are introduced

Step 2: Configuration files

spring.application.name=spring-boot-redis
server.port=9090
Set session storage mode, use Redis storage
spring.session.store-type=redis
The session duration is 15 minutes
server.servlet.session.timeout=PT15M
# # Redis configuration
## Redis database index
spring.redis.database=1
# Redis server address
spring.redis.host=127.0.0.1
Redis server connection port
spring.redis.port=6379
## Redis server connection password (default null)
spring.redis.password=
Copy the code

Step 3: Implement the logic

Initialize user data

@Slf4j
@RestController
@RequestMapping(value = "/user")
public class UserController {

    Map<String, User> userMap = new HashMap<>();

    public UserController(a) {
        // Initialize 1 user to simulate login
        User u1=new User(1."user1"."user1");
        userMap.put("user1",u1); }}Copy the code

Instead of using the database, initialize two pieces of data to simulate a login

The login

 @GetMapping(value = "/login")
    public String login(String username, String password, HttpSession session) {
        // simulate database lookup
        User user = this.userMap.get(username);
        if(user ! =null) {
            if(! password.equals(user.getPassword())) {return "Wrong username or password!!";
            } else {
                session.setAttribute(session.getId(), user);
                log.info("Login successful {}",user); }}else {
            return "Wrong username or password!!";
        }
        return "Login successful!!";
    }

Copy the code

The login interface is used for authentication. If the authentication succeeds, use session.setattribute (session.getid (), user). Put relevant information into session.

To find the user

    /** * Find user */ by user name
    @GetMapping(value = "/find/{username}")
    public User find(@PathVariable String username) {
        User user=this.userMap.get(username);
        log.info("Find user {} by username ={}",username,user);
        return user;
    }

Copy the code

Emulation finds users by username

Access to the session

  /** * take the current user's session */
    @GetMapping(value = "/session")
    public String session(HttpSession session) {
        log.info("Current user session={}",session.getId());
        return session.getId();
    }
Copy the code

Log out

  /** * Log out */
    @GetMapping(value = "/logout")
    public String logout(HttpSession session) {
        log.info("Exit login session={}",session.getId());
        session.removeAttribute(session.getId());
        return "Successful exit!!";
    }
Copy the code

When you exit, you delete the user information from the session.

Step 4: Write the Session interceptor

The session interceptor checks whether the request sent by the current user has a session ID. If not, the user is prompted to log in again.

 @Configuration
    public class SecurityInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
            HttpSession session = request.getSession();
            // Check whether the current session exists. True is returned if the current session exists. True indicates that service logic can be processed normally
            if(session.getAttribute(session.getId()) ! =null){
                log.info("Session interceptor, session={}, authenticated",session.getId());
                return true;
            }
            // Session does not exist, returns false and prompts you to log in again.
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            response.getWriter().write("Go to ļ¼ļ¼ļ¼ļ¼ļ¼");
            log.info("Session interceptor, session={}, authentication failed",session.getId());
            return false; }}Copy the code

Step 5: Inject interceptors into the interceptor chain

@Slf4j
@Configuration
public class SessionCofig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SecurityInterceptor())
                // Exclude two blocked paths
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/user/logout")
                // Intercept all URL paths
                .addPathPatterns("/ * *"); }}Copy the code

Step 6: Test

The login user user1: http://127.0.0.1:9090/user/login? username=user1&password=user1

Query user1 user session: http://127.0.0.1:9090/user/session

Logged out: http://127.0.0.1:9090/user/logout

View data in Redis after login:

The Seesion data has been saved to Redis, where we have integrated the distributed Seesion functionality using Spring-Seesion.

Redis distributed Cache (14) — Distributed session inconsistency Solution — Mining gold (juejin. Cn)

Write in the last

  • šŸ‘šŸ» : have harvest, praise encouragement!
  • ā¤ļø : Collect articles, easy to look back!
  • šŸ’¬ : Comment exchange, mutual progress!