Web project (2) —— Realize registration and login

content:

1. Implementation overview

2. Implementation of registration function

3. Implementation of login function

4. Status judgment

 

 

1. Implementation overview:

General idea : After registration and login, the server will generate a short-term valid ticket, and store the ticket in the browser's cookie, and then use Interception to render the web page according to the ticket in the cookie.

configuration.ToutiaoWebConfiguration : used to associate an interceptor with a web page.

controller.LoginController : Front-end interaction for registration and login operations.

model.LoginTicket and dao.LoginTicketDAO : The ticket issued by the server to the logged-in user, that is, the user's logged-in credentials.

model.HostHolder : All properties are defined as static ThreadLocal, which can realize multi-threading (multiple users access the website at the same time).

interception.PassportInterception : interception interceptor, which is an aspect-oriented idea. This class is used to determine whether you have logged in before loading a web page.

interception.LoginRequiredInterception : Set the permission page, if the permission is not enough, it will automatically jump back to the home page.

Service.UserService : used to implement the logical judgment of registration and login.

 

 

 

2. Registration function implementation:

Implement logical judgment in UserService:

public Map<String ,Object> Register(String username, String password){
        Map<String ,Object> map=new HashMap<>();
        if(StringUtils.isBlank(username)){
            map.put("msgname","用户名不能为空");
            return map;
        }
        if(StringUtils.isBlank(username)){
            map.put("msgpassword","密码不能为空");
            return map;

        }
        User user=userDAO.selectByName(username);
        if(user!=null){
            map.put("msgname","用户名已经被注册");
            return map;

        }
        user=new User();
        user.setName(username);
        user.setSalt(UUID.randomUUID().toString().substring(0,5));//为了进一步加密密码而生成的随机数
        user.setHeadUrl(String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)));
        user.setPassword(ToutiaoUtil.MD5(password+user.getSalt()));//使用MD5加密password+salt更加安全
        userDAO.addUser(user);

        //注册结束直接登录
        String ticket=addLoginTicket(user.getId());
        map.put("ticket",ticket);

        return map;
    }



private String addLoginTicket(int userId){
        LoginTicket ticket=new LoginTicket();
        ticket.setUserId(userId);

        Date date=new Date();
        date.setTime(date.getTime()+1000*3600*24);
        ticket.setExpired(date);//设置ticket的过期时间

        ticket.setStatus(0);//用于标记当前ticket是否有效,0为有效,1为失效
        ticket.setTicket(UUID.randomUUID().toString().replaceAll("-",""));

        loginTicketDAO.addTicket(ticket);//存储它的ticket以便于之后一段时间的自动登录的验证

        return ticket.getTicket();
    }

3. Login function implementation:

Basically the same as the registration function.

public Map<String ,Object> login(String username, String password){
        Map<String ,Object> map=new HashMap<>();
        if(StringUtils.isBlank(username)){
            map.put("msgname","用户名不能为空");
            return map;
        }
        if(StringUtils.isBlank(username)){
            map.put("msgpassword","密码不能为空");
            return map;

        }
        User user=userDAO.selectByName(username);
        if(user==null){
            map.put("msgname","用户名不存在");
            return map;

        }
        if(!ToutiaoUtil.MD5(password+user.getSalt()).equals(user.getPassword())){//验证密码
            map.put("msgpassword","密码错误");
            return map;
        }


        //登录验证完,自动登录,即下发ticket
        String ticket=addLoginTicket(user.getId());
        map.put("ticket",ticket);


        return map;
    }

 

4. Status judgment:

The logical judgment of registration and login is easy to implement, but how do we let the browser remember whether we are logged in or who we are when we open or refresh a page? What about these questions. Therefore, we need two parts to solve these problems: ①HostHandler class to record who the currently logged in user is. ②Interception (interceptor), before each webpage is loaded, reads the cookie to determine who we are. ③In the LoginController, the ticket is written into the cookie.

①The implementation class of HostHandle is very simple. It should be noted that its member variable user (that is, "who am I") needs to be set to ThreadLocal to deal with multi-threading problems; its method is set to static, which is convenient for calling.

package com.nowcoder.model;


import org.springframework.stereotype.Component;

@Component
public class HostHolder {
    private static ThreadLocal<User> users=new ThreadLocal<>();//解决“我是谁”的问题,ThreadLocal解决多线程问题

    public static User getUser(){//定义成static方便调用
        return users.get();
    }

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

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

}

②Interception (interceptor)

PassportInterception can implement a series of judgments and operations before and after web page loading. Need to implement the HandlerInterception interface, this interface has three methods.

//HandlerInterceptor.java

public interface HandlerInterceptor {
    boolean preHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
//该方法将在请求处理之前进行调用,只有该方法返回true,才会继续执行后续的Interceptor和Controller,当返回值为true 时就会继续调用下一个Interceptor的preHandle 方法,如果已经是最后一个Interceptor的时候就会是调用当前请求的Controller方法; 

    void postHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4) throws Exception;
//该方法将在请求处理之后,DispatcherServlet进行视图返回渲染之前进行调用,可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。 

    void afterCompletion(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4) throws Exception;
//该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。用于进行资源清理。
}

 

package com.nowcoder.interceptor;

//PassportInterceptor.java

import com.nowcoder.dao.LoginTicketDAO;
import com.nowcoder.dao.UserDAO;
import com.nowcoder.model.HostHolder;
import com.nowcoder.model.LoginTicket;
import com.nowcoder.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

@Component
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;
        if(httpServletRequest.getCookies()!=null){//读取httpServletRequest的cookie
            for(Cookie cookie: httpServletRequest.getCookies()){//遍历所有cookie
                if( cookie.getName().equals("ticket")){//如果有cookie等于ticket
                    ticket=cookie.getValue();//取该cookie的value
                    break;
                }
            }
        }
        if(ticket!=null){
            LoginTicket loginTicket=loginTicketDAO.selectByTicket(ticket);//在数据库中查找该ticket
            if(loginTicket==null||loginTicket.getExpired().before(new Date())||loginTicket.getStatus()!=0){//判断loginTicket是否为空,是否过期,是否失效
                return true;
                //直接进入posthandle,继续判断是否有hosthandle
            }

            User user=userDAO.selectById(loginTicket.getUserId());//根据Loginticket中的userID属性查找对应的user
            hostHolder.setUser(user);//将该user存入hostHolder中
        }
        return true;
        //直接进入posthandle,继续判断是否有hosthandle
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        if(modelAndView!=null&&hostHolder.getUser()!=null){//判断hostHolder是否为空,即判断是否有当前登录用户
            //modelAndView可以实现后端与前端的交互,addObject的元素可以直接被前端模板调用
            //html文件中可以直接使用这个$user
            modelAndView.addObject("user",hostHolder.getUser());
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        hostHolder.clear();//网页关闭,清理hostHolder占用的内存
    }
}

The interception is written here, its function has been implemented but it needs to be associated with this website, using configuration.ToutiaoWebConfiguration to achieve

package com.nowcoder.configuration;

import com.nowcoder.interceptor.LoginRequiredInterceptor;
import com.nowcoder.interceptor.PassportInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Component//用@Component对那些比较中立的类进行凝视,让spring可以自动装载这个类
public class ToutiaoWebConfiguration extends WebMvcConfigurerAdapter {
    @Autowired
    PassportInterceptor passportInterceptor;

    @Autowired
    LoginRequiredInterceptor loginRequiredInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(passportInterceptor);//将passportInterceptor注册,才能回调
        registry.addInterceptor(loginRequiredInterceptor).addPathPatterns("/setting/");//不是全局的,只是setting页面
        super.addInterceptors(registry);
    }
}

 

③LoginController

Write the ticket into a cookie, take registration as an example, and the same is true for login.

    @RequestMapping(path = {"/reg/"}, method = {RequestMethod.GET, RequestMethod.POST})
    //设置网页路径为/reg/
    @ResponseBody
    //@responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,
    //写入到response对象的body区,通常用来返回JSON数据或者是XML
    public String reg(Model model , @RequestParam("username") String username,
                      @RequestParam("password") String password,
                      @RequestParam(value = "rember",defaultValue = "0") int remeberme,
                      HttpServletResponse response) {
//此处没有写前端,所以用户名,密码需要在url中填写
        try {
            Map<String,Object> map=userService.Register(username,password);
            //调用注册功能,得到注册返回的map
            if (map.containsKey("ticket")){
                //将ticket写入cookie
                Cookie cookie=new Cookie("ticket",map.get("ticket").toString());
                cookie.setPath("/");//设置cookie为全站有效的

                //设置remember me,即延长cookie的保存时间,,否则默认浏览器关闭删除cookie
                if(remeberme>0){
                    cookie.setMaxAge(3600*24*5);
                }

                response.addCookie(cookie);//注意设置完cookie的所有属性才能add到response,否则不会提交


                return ToutiaoUtil.getJSONString(0,"注册成功");//返回json格式的信息字符串
            }
            else{
                return ToutiaoUtil.getJSONString(1,map);//返回json格式的信息字符串
            }
        }catch (Exception e){
            logger.error("注册异常"+e.getMessage());
            return ToutiaoUtil.getJSONString(1,"注册异常");//返回json格式的信息字符串
        }
    }

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325467118&siteId=291194637