[intermediate project of niuke.com] lesson 4 registration, login and browsing security

[intermediate project of niuke.com] lesson 4 registration, login and browsing security

Catalog

Article directory

1. registration

1.1 registration function:

1. User name validity detection

2. password length requirements

3. Password Salt encryption, password strength detection

4. User email / SMS activation

1.2 code function realization:

UserDAO section

Add the function of querying whether the user is registered to the database in UserDAO

    @Select({"select ", SELECT_FIELDS, " from ", TABLE_NAME, " where name=#{name}"})
    User selectByName(String name);

UserService section

register function is added to UserService to verify whether the user name and password are empty, whether the user is registered, and whether the password is generated (salt adding algorithm). The information returned to the Controller is stored in Map, and the Controller generates a unified JSON format to return to the front end
Verify user name and password:

        Map<String, Object> map = new HashMap<String, Object>();
        if (StringUtils.isBlank(username)) {
            map.put("msgname", "User name cannot be empty");
            return map;
        }

        if (StringUtils.isBlank(password)) {
            map.put("msgpwd", "Password cannot be empty");
            return map;
        }

To query the database to see if the user has been registered:

        User user = userDAO.selectByName(username);

        if (user != null) {
            map.put("msgname", "User name is already registered");
            return map;
        }

Password generation:
md5 + salt encryption algorithm is used for password generation. Ordinary md5 algorithm can't get the original string by decoding. If you need to verify whether the password is correct, you need to do md5 encryption on the verification password and then compare it with the password in the database.

But for the sake of security, we need to add salt (salt) to the password. Salt is essentially a randomly generated string, and then spliced with the password. Finally, MD5 encryption algorithm is used as a whole.
We set up a tool class, ToutiaoUtil
MD5 algorithm is as follows:

public static String MD5(String key) {
        char hexDigits[] = {
                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
        };
        try {
            byte[] btInput = key.getBytes();
            // Get the MessageDigest object of MD5 digest algorithm
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            // Update summary with specified bytes
            mdInst.update(btInput);
            // Get ciphertext
            byte[] md = mdInst.digest();
            // Convert ciphertext to hexadecimal string form
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            logger.error("generate MD5 fail", e);
            return null;
        }
    }

Add salt to password:

// Password strength
        user = new User();
        user.setName(username);
        //UUID library randomly generates a string and intercepts the first 5 bits
        user.setSalt(UUID.randomUUID().toString().substring(0, 5));
        String head = String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000));
        user.setHeadUrl(head);
        user.setPassword(ToutiaoUtil.MD5(password+user.getSalt()));
        userDAO.addUser(user);
        //Register successfully and issue ticket in the background to log in
        String ticket = addLoginTicket(user.getId());
        map.put("ticket", ticket);
        return map;

The successful registration means that the user has a token that can prove his / her identity, which needs to be distributed to the server. Then the server authenticates the user's identity with the token, which is bound with the user id. for detailed implementation, refer to the login layer.

LoginController section

The Controller layer judges after receiving the Map information of the UserService layer. If the ticket table is successfully logged in, the ticket is saved in the cookie and the response is returned, and the JSON format information of successful registration is returned to the front end.
The code is as follows:

    @RequestMapping(path = {"/reg/"}, method = {RequestMethod.GET, RequestMethod.POST})
    @ResponseBody
    public String reg(Model model, @RequestParam("username") String username,
                      @RequestParam("password") String password,
                      @RequestParam(value="rember", defaultValue = "0") int rememberme,
                      HttpServletResponse response) {
        try {
            Map<String, Object> map = userService.register(username, password);
            if (map.containsKey("ticket")) {
                Cookie cookie = new Cookie("ticket", map.get("ticket").toString());
                //Total station effectiveness
                cookie.setPath("/");
                //Select 'remberme'cookie with a 5-day lifetime
                if (rememberme > 0) {
                    cookie.setMaxAge(3600*24*5);
                }
                response.addCookie(cookie);
                return ToutiaoUtil.getJSONString(0, "login was successful");
            } else {
                return ToutiaoUtil.getJSONString(1, map);
            }

        } catch (Exception e) {
            //Return to front-end msg in case of exception
            logger.error("Registration exception" + e.getMessage());
            return ToutiaoUtil.getJSONString(1, "Registration exception");
        }

2. login

2.1 login function:

1. server password verification / three party verification callback, token registration

1.1 server token Association userid
1.2 client store token

2. Server / client token validity setting

Note: a token can be a sessionid or a Key in a cookie

2.2 code function realization:

LoginTicketDAO section

Create the LoginTicketDAO class to implement the following functions: adding tickets when users log in, finding tickets when browsing web pages, and updating tickets when they log out.

    @Insert({"insert into ", TABLE_NAME, "(", INSERT_FIELDS,
            ") values (#{userId},#{expired},#{status},#{ticket})"})
    int addTicket(LoginTicket ticket);

    @Select({"select ", SELECT_FIELDS, " from ", TABLE_NAME, " where ticket=#{ticket}"})
    LoginTicket selectByTicket(String ticket);

    @Update({"update ", TABLE_NAME, " set status=#{status} where ticket=#{ticket}"})
    void updateStatus(@Param("ticket") String ticket, @Param("status") int status);

UserService section

The user service part adds the login function. The main functions are to verify whether the user name and password are empty, whether the user name exists, and whether the password is correct. After successful login, token should be associated with userid
First, create a LoginTicket class in the model. The member variables of the class include id, userid, expiration date, and status(0 means valid, 1 means invalid)
Login successfully added ticket to database:

LoginTicket ticket = new LoginTicket();
        ticket.setUserId(userId);
        Date date = new Date();
        date.setTime(date.getTime() + 1000*3600*24);
        ticket.setExpired(date);
        ticket.setStatus(0);
        ticket.setTicket(UUID.randomUUID().toString().replaceAll("-", ""));
        loginTicketDAO.addTicket(ticket);
        return ticket.getTicket();

LoginController section

Similar to registration, save ticket s to cookie s

    public String login(Model model, @RequestParam("username") String username,
                      @RequestParam("password") String password,
                      @RequestParam(value="rember", defaultValue = "0") int rememberme) {
        try {
            Map<String, Object> map = userService.register(username, password);
            if (map.containsKey("ticket")) {
                Cookie cookie = new Cookie("ticket", map.get("ticket").toString());
                cookie.setPath("/");
                if (rememberme > 0) {
                    cookie.setMaxAge(3600*24*5);
                }
                return ToutiaoUtil.getJSONString(0, "login was successful");
            } else {
                return ToutiaoUtil.getJSONString(1, map);
            }

        } catch (Exception e) {
            logger.error("Registration exception" + e.getMessage());
            return ToutiaoUtil.getJSONString(1, "Registration exception");
        }

3. Browse page (key)

3.1 registration function:

1. Client: HTTP request with token

Client users log in to browse the web

2. Server:

The server authenticates the user according to the token and controls the authority

1. Get the user id according to the token
2. Obtain the specific information of the user according to the user id
3. User and interface access rights processing
4. Rendering interface / jump page

3.2 code function realization:

Interceptor (Interceptor)


Spring MVC allows web requests to be intercepted through a handler interceptor. The handler interceptor must implement the three methods contained in the HandleInterceptor:
1.preHandle(): after the HTTP request is submitted and before it reaches the Controller, a Boolean value is returned. true means to continue the execution chain of the handler, and false means to stop the execution.
2. Posthandle(): after controller, before rendering page
3.afterCompletion(): the end of the whole request.
Three methods to implement HandlerInterceptor

public class PassportInterceptor implements HandlerInterceptor {

    @Autowired
    private LoginTicketDAO loginTicketDAO;

    @Autowired
    private UserDAO userDAO;

    @Autowired
    private HostHolder hostHolder;
    
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,Object o) throws Exception{
        String ticket = null;
        //After the user sends the request, find the ticket in the cookie from the request
        if(httpServletRequest.getCookies()!=null) {
            for(Cookie cookie:httpServletRequest.getCookies()){
                if(cookie.getName().equals("ticket")){
                    ticket = cookie.getValue();
                    break;
                }
            }
        }
        //There are ticket s, but they can't be believed (possibly forged). Check the database
        if(ticket != null) {
            LoginTicket loginTicket = loginTicketDAO.selectByTicket(ticket);
        }
        //The database cannot be queried, the ticket validity period is invalid, and the ticket status is invalid
        if(loginTicket == null || loginTicket.getExpired().before(new Date()) || loginTicket.getStatus()!=0) {
            return true;
        }
         //Determine the user identity according to the ticket, and the hostHolder saves the user
         User user = userDAO.selectById(loginTicket.getUserId());
         hostHolder.setUser(user);
    }
    
    //Before rendering the page, modelAndView adds user to the front end
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        if (modelAndView != null && hostHolder.getUser() != null) {
            modelAndView.addObject("user", hostHolder.getUser());
        }
    }
    //Clear hostHolder the user process
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        hostHolder.clear();
    }

Create a new hostHolder class to save the thread user

@Component
public class HostHolder {
    private static ThreadLocal<User> users = new ThreadLocal<User>();

    public User getUser() {
        return users.get();
    }

    public void setUser(User user) {
        users.set(user);
    }

    public void clear() {
        users.remove();
    }
}

Jump not logged in

We also need to control the rights of users who are not logged in, restrict access to certain pages, and set up LoginRequireInterceptor

@Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        if (hostHolder.getUser() == null) {
            httpServletResponse.sendRedirect("/?pop=1");
            return false;
        }
        return true;
    }

Register interceptor

Suspend the whole link, register the interceptor, and create a new configuration in which touriowebconfiguration inherits WebMvcConfigurerAdapter

@Autowired
PassportInterceptor passportInterceptor;

@Autowired
LoginRequiredInterceptor loginRequiredInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(passportInterceptor);
    //Specify the scope of loginRequiredInterceptor
    registry.addInterceptor(loginRequiredInterceptor).addPathPatterns("/setting*");
    super.addInterceptors(registry);
}

4. User data security

Multiple channels:

1.HTTP registration page

2. public key encryption, private key decryption, Alipay h5 page payment password encryption

3. The user password salt prevents cracking (CSDN, Netease email does not encrypt password disclosure)

4. Validity of token

5. Single sign on of a single platform and abnormal IP login

6. Authority judgment of user status

7. Add verification code mechanism to prevent explosion and batch registration

5.AJAX asynchronous data interaction

For example, if you want to turn a comment page, do not refresh the page, only load the comment and update the comment area

Benefits:

1. Do not refresh the page

2. Better experience

3. Less data transfer

4.APP / website general

Extension: unified data format: {code:0,msg:",data:"}
Example: log in box for ox guest delivery, click like log in box

6.SpringBoot Dev Tools

Add the spring bootdevtools dependency, and you can only modify and run a file without restarting the entire project

Published 1 original article, praised 0 and visited 1
Private letter follow

Keywords: Database less SpringBoot JSON

Added by bostonmacosx on Mon, 10 Feb 2020 11:34:05 +0200