User management is a problem that most Web sites need to solve. User management involves user registration and login.
User registration is relatively simple, we can first through the API to the user registration this function to achieve:
_RE_MD5 = Re.compile (R ' ^[0-9a-f]{32}$ ') @api @post ('/api/users ') def register_user (): i = Ctx.request.input (name= ", Email= ", password=") name = I.name.strip () email = I.email.strip (). lower () Password = I.password if not name: raise Apivalueerror (' name ') if not e-mail or not _re_email.match (e-mail): raise Apivalueerror (' email ') if not password or not _re_md5.match (password): raise Apivalueerror (' password ') user = User.find_first (' Where email=? ', email) if User: Raise Apierror (' register:failed ', ' email ', ' email is already in use ') user = User (name=name, email=email, password=p Assword, image= ' http://www.gravatar.com/avatar/%s?d=mm&s=120 '% hashlib.md5 (email). Hexdigest ()) User.insert () return user
Note The user password is a MD5 computed 32-bit hash string that is passed by the client, so the server side does not know the user's original password.
Next, you can create a registration page that allows users to fill out a registration form and then submit data to the registered user's API:
Welcome to Register!
{% Endblock%} Try
This allows us to complete the functionality of the user registration:
User login is more complex than user registration. Because the HTTP protocol is a stateless protocol, and the server is tracking the user state, it can only be implemented through cookies. Most Web frameworks provide session functionality to encapsulate cookies that hold user state.
The advantage of the session is that it is easy to use and can remove user login information directly from the session.
The disadvantage of the session is that the server needs to maintain a mapping table in memory to store user login information, if there are more than two servers, you need to cluster the session, so the Web App using session is difficult to expand.
We use a direct read cookie to verify the user's login, each time the user accesses any URL, the cookie will be verified, the advantage is to ensure that the server processing any URL is stateless, can be extended to more than one server.
Because the server generates a cookie to send to the browser after successful login, make sure that the cookie is not spoofed by the client.
The key to implementing anti-counterfeiting cookies is through a one-way algorithm such as MD5, for example:
When the user enters the correct password to log in successfully, the server can fetch the user's ID from the database and calculate a string as follows:
"User ID" + "Expiration Time" + MD5 ("User ID" + "User password" + "expiry time" + "Secretkey")
When the browser sends a cookie to the server side, the information the server can get is as follows:
- User ID
- Expiry time
- MD5 value
If the expiration time is not reached, the server looks up the user's password based on the user ID and calculates:
MD5 ("User ID" + "User password" + "Expiration Time" + "Secretkey")
And compared to the MD5 in the browser cookie, if it is equal, the user is logged in, otherwise, the cookie is forged.
The key to this algorithm is that MD5 is a one-way algorithm that calculates MD5 from the original string, but fails to eject the original string through MD5.
So the login API can be implemented as follows:
@api @post ('/api/authenticate ') def authenticate (): i = ctx.request.input () email = I.email.strip (). lower () Password = I. Password user = User.find_first (' Where email=? ', email) if User is None:raise apierror (' auth:failed ', ' email ', ' Inva Lid email. ') Elif User.password! = password:raise apierror (' auth:failed ', ' Password ', ' Invalid password. ') Max_age = 604800 cookie = Make_signed_cookie (user.id, User.password, Max_age) Ctx.response.set_cookie (_cookie_name, COO Kie, max_age=max_age) User.password = ' ****** ' return user# COMPUTE encryption cookie:def make_signed_cookie (ID, password, max_age): E Xpires = str (int (time.time () + max_age)) L = [ID, expires, hashlib.md5 ('%s-%s-%s-%s '% (ID, password, expires, _cookie_ke Y). Hexdigest ()] return '-'. Join (L) for each URL handler, if we all write code that parses the cookie, it causes the code to repeat many times. Use the interceptor to parse the cookie before processing the URL and bind the logged-in user to the Ctx.request object so that subsequent URL handlers can be directly accessible to the logged-on User: @interceptor ('/') def user_ Interceptor (Next): User = None cookie = ctx.request.cookies.get (_cookie_name) if Cookie:useR = Parse_signed_cookie (cookie) ctx.request.user = user return Next () # decryption Cookie:def Parse_signed_cookie (COOKIE_STR): TR Y:l = Cookie_str.split ('-') If Len (L)! = 3:return None ID, expires, MD5 = L if INT (expires) < TIME.T IME (): return None user = User.get (ID) If user is None:return None if MD5! = HASHLIB.MD5 ('%s-%s-%s-%s ' % (ID, User.password, expires, _cookie_key)). Hexdigest (): Return None return user Except:return Nonetry
In this way, we have completed the user registration and login functions.