ショッピングモール秒殺システムの実現(二)登録機能の実現

34205 ワード

文書ディレクトリ
  • データベース設計
  • 暗号化:2回MD 5
  • pom.xmlに依存を追加:
  • はloginを記述する.html
  • はjqueryを導入する.js、bootstrap、jquery-validation、layer.js
  • domainで対応するuser
  • を作成
  • パラメータチェック
  • カスタムパラメータ検証器
  • login部分コード解析
  • グローバル例外プロセッサ
  • 分散セッション
  • 原理
  • ログインプロセス:
  • 具体的には
  • を実現する.
  • redisにおけるsetメソッドの実装
  • addCookie法の実現
  • 完全コード
  • データベース設計
    CREATE TABLE miaosha_user (
      `id` bigint(20) NOT NULL COMMENT '  ID,    ',
      `nickname` varchar(255) NOT NULL,
      `password` varchar(32) DEFAULT NULL COMMENT 'MD5(MD5(pass  +  salt) + salt)',
      `salt` varchar(10) DEFAULT NULL,
      `head` varchar(128) DEFAULT NULL COMMENT '  ,    ID',
      `register_date` datetime DEFAULT NULL COMMENT '    ',
      `last_login_date` datetime DEFAULT NULL COMMENT '      ',
      `login_count` int(11) DEFAULT '0' COMMENT '    ',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    
    
    

    暗号化:MD 5 2回
    MD 5ツールクラスの導入、MD 5 Utilの追加
    httpは明文で登録されており、安全ではありません
  • まずクライアントmd 5は、サービス側
  • に再伝送される.
  • サービス側は、受信後+salt再md 5をデータベースに書き込む(データベースの盗難防止)
  • .
    pom.xmlに依存を追加するには:
    <dependency>
     <groupId>commons-codecgroupId>
     <artifactId>commons-codecartifactId>
    dependency>
    <dependency>
     <groupId>org.apache.commonsgroupId>
     <artifactId>commons-lang3artifactId>
     <version>3.6version>
    dependency>
    

    作成login.html
    (コード表示github)
    jqueryを導入するjs、bootstrap、jquery-validation、layer.js
    (ファイルはgithubを参照)
    domainで対応するuserを作成する
    
    public class MiaoshaUser {
    	private Long id;
    	private String nickname;
    	private String password;
    	private String salt;
    	private String head;
    	private Date registerDate;
    	private Date lastLoginDate;
    	private Integer loginCount;
    }
    

    パラメータチェツク
    スプリングboot-starter-validationの導入
    <dependency>
          <groupId>org.springframework.bootgroupId>
          <artifactId>spring-boot-starter-validationartifactId>
    dependency>
    

    カスタムパラメータ検証
    自己定義:
    validator:
          @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})  
          @Retention(RetentionPolicy.RUNTIME)  
          @Documented  
          @Constraint(validatedBy = IsMobileValidator.class)//   ismobilexxx
          public @interface IsMobile {
              String message() default "        ";  
              Class<?>[] groups() default {};  
              Class<? extends Payload>[] payload() default {};  
              boolean required() default true;  //    
          }
          public class IsMobileValidator implements ConstraintValidator<IsMobile, String>{
          }
    

    IsMobileValidator
         public class IsMobileValidator extends ConstraintValidator<>(){
         	private boolean required = false;
           public void initialize(IsMobile constraintAnnotation){
         		required = 
           }
           public boolean isValid(){
         		if(required){
         			return ValidatorUtil.isMobile
             }
             else{
         			if(StringUtils.isEmpty(value)){
         				return true;
               }
               else {
         				return ValidatorUtil.isMobile(value);
               }
             }
           }
         }
    

    login部分コード解析
    	public boolean login(HttpServletResponse response,LoginVo loginVo) {
    		if(loginVo == null) {
    			throw new GlobalException(CodeMsg.SERVER_ERROR); 
    		}
    		String mobile = loginVo.getMobile();
    		String formpass = loginVo.getPassword();
    		MiaoshaUser user = getById(Long.parseLong(mobile));//        
    		if(user == null) {
    			throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
    		}
    		//    
    		String dbPass = user.getPassword();//          
    		String saltDB = user.getSalt();//        salt
    		String calcPass = MD5Util.formPass2DBPass(formpass, saltDB);//  salt        calc  	
    		if(!calcPass.equals(dbPass)) {//        
    			throw new GlobalException(CodeMsg.PASSWORD_ERROR);
    		}
    }
    

    グローバル例外プロセッサ
    @ControllerAdvice
    @ResponseBody
    
    @ExceptionHandler(value=Excption.class)
    public Result<String> excptionHandler(HttpServletRequest request ,Exeption e){
      if(e instanceof BindException){
    		BindException ex = (BindException)e;
        List<ObjectError> errors = ex.getAllErrors();
        return Result.error(CodeMsg.BIND_EORROR.fillArgs());
      } 
    } 
    
    
    @ControllerAdvice
    @ResponseBody
    public class GlobalExceptionHandler {
        @ExceptionHandler(value=Exception.class)  
        public Result<String> allExceptionHandler(HttpServletRequest request, Exception exception) throws Exception{  
        }
    }
    

    分散セッション
    げんり
    実際の実装では,1台のサーバだけではなく,1回目のリクエストが1つ目のサーバに到達し,2回目のリクエストが2つ目のサーバに到達すると,1回目のセッション情報が失われるので,セッション情報をredisに格納する.
    ログインプロセス:
    ログイン後、to_にジャンプリストページがジャンプするときにパラメータを持ってきて、クッキー(またはrequestのパラメータ)を使って、to_リストでは、クッキーのパラメータを取り出し、パラメータを介してredisからuserがこのuserを示す貨物インタフェースを取得します.
    ユーザ情報をredisに格納するにはtoken一意の識別が必要である(ログインに成功した後、このユーザにsessionIDのようなものを生成し、このユーザを表示し、cookieに書いてクライアントに伝え、クライアントはその後のアクセスでcookieでtokenを伝え、サービス側はこのtokenを取得した後、このtokenでユーザ対応のsession情報を取得する)
    具体的な実装
    redisにおけるsetメソッドの実装
    	//    
    	//  :  +key+value
    	public <T> boolean set(KeyPrefix prefix, String key,  T value) {
    		 Jedis jedis = null;
    		 try {
    			 jedis = jedisPool.getResource();
    			 String str = beanToString(value);
    			 if(str == null || str.length() <= 0) {
    				 return false;
    			 }
    			//     key
    			 String realKey = prefix.getPrefix() + key;
    			 int seconds =  prefix.expireSeconds();
    			 if(seconds <= 0) {
    				 jedis.set(realKey, str);
    			 }else {
    				 jedis.setex(realKey, seconds, str);
    			 }
    			 return true;
    		 }finally {
    			  returnToPool(jedis);
    		 }
    	}
    

    addCookieメソッドの実装
    クッキーに書く:token=>cookieこのtokenがどのユーザに対応しているかを示すため、このユーザ情報をredisに書く(サードパーティキャッシュ)ことで、tokenを知るだけでユーザ情報が得られる
    	//  cookie response ,redis   
    	private void addCookie(HttpServletResponse response, String token, MiaoshaUser user) {
    		//1. redis   
    		//      set     :  +key+value
    		//MiaoshaUserKey.token        ,   MiaoshaUserKey   
    		redisService.set(MiaoshaUserKey.token, token, user);
    		//2.  cookie response 
    		Cookie cookie = new Cookie(COOKIE_NAME_TOKEN, token);
    		//              
    		cookie.setMaxAge(MiaoshaUserKey.token.expireSeconds());
    		cookie.setPath("/");
    		response.addCookie(cookie);
    	}
    

    完全なコード
    上のloginと最後のaddCookieの部分です
    	public boolean login(HttpServletResponse response,LoginVo loginVo) {
    		if(loginVo == null) {
    			throw new GlobalException(CodeMsg.SERVER_ERROR); 
    		}
    		String mobile = loginVo.getMobile();
    		String formpass = loginVo.getPassword();
    		MiaoshaUser user = getById(Long.parseLong(mobile));//        
    		if(user == null) {
    			throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
    		}
    		//    
    		String dbPass = user.getPassword();//          
    		String saltDB = user.getSalt();//        salt
    		String calcPass = MD5Util.formPass2DBPass(formpass, saltDB);//  salt        calc  	
    		if(!calcPass.equals(dbPass)) {//        
    			throw new GlobalException(CodeMsg.PASSWORD_ERROR);
    		}
    		//    token(   immutable universally unique identifier )
    		String token = UUIDUtil.uuid();
    		addCookie(response, token, user);
    		return true;
    	}