I’m participating in nuggets Creators Camp # 4, click here to learn more and learn together!

Demand presentation

Do you have the need for database encryption, but do not want to encrypt and decrypt data in the business code?

Colleagues using Java can use Mybatis interceptor to quickly achieve data encryption and decryption, so golang has such a convenient method?

Congratulations, Gorm hooks also make it easy to encrypt data

GORM of hook

Hooks are functions that are called before and after operations such as create, query, update, delete, and so on.

If you have defined the specified method for the model, it is automatically called when the model is created, updated, queried, or deleted. If any callbacks return an error, GORM stops subsequent operations and rolls back the transaction.

Note that save and delete operations in GORM run on a transaction by default, so changes made in that transaction are not visible until the transaction completes and will be rolled back if your hook returns any errors.

1. Insert hooks that can be used by data

// Start transaction BeforeSave BeforeCreate // Save before correlation // Insert record to DB // Save AfterCreate AfterSave // Commit or rollback transactionCopy the code

2. The hooks that can be used to update data

// Start transaction BeforeSave BeforeUpdate // Save before association // Update DB // Save AfterUpdate AfterSave // Commit or rollback transactionCopy the code

3. Delete the available data tick

// Start transaction BeforeDelete // Delete data in DB AfterDelete // Commit or roll back transactionCopy the code

4. Hooks that can be used to query data

// Load data from db // Preloading (eager loading) AfterFindCopy the code

Code demo

The code shown below is my diary application, mainly encrypts the user’s diary information

Import ("encoding/base64" "errors" "FMT" "SMS /service/ SRC /utils" "time" "github.com/jinzhu/gorm") type UserDiary struct { Id int64 `json:"id" ` //id serial primary key, Userid int64 `json:"userid" ` //userid integer not null , Year int64 `json:"year" ` //year integer not null , Month int64 `json:"month" ` //month integer not null , Day int64 `json:"day" ` //day integer not null , CreateTime time.Time `json:"create_time" ` //create_time timestamp default CURRENT_TIMESTAMP , UpdateTime time.Time `json:"update_time" ` //update_time timestamp default CURRENT_TIMESTAMP, Status int64 `json:"status" ` //status int not null default 0, Color string `json:"color" ` //color varchar(32) not null default 'orange', Diary String 'json:" Diary "ENC :" AES"' // Diary text} // Save the encrypted data func (Diary *UserDiary) BeforeSave(db * gorm.db) (err) error) { utils.Log.Infof("Call UserDiary BeforeSave [%s]", diary.Diary) if diary.Diary ! Sprintf("user_code_%v", diary.Userid) key, ok := utils.getCache (keyName) // Each user has its own private key, The cache key here comes from if! ok { utils.Log.Errorf("GetCache(%s) failed!" , keyname) return errors.New(" Failed to get user key code!" ) } rs, err := utils.AesEcrypt([]byte(diary.Diary), []byte(utils.MD5(key.(string)))) if err ! = nil { utils.Log.Errorf("AesEcrypt(%s) [%d] failed! %v", diary.Diary, diary.Userid, Err) return err} diary. A diary. = base64 StdEncoding. EncodeToString return (rs)}} / / return to decrypt data before func (diary * UserDiary) AfterFind(tx *gorm.DB) (err error) { utils.Log.Info("Call UserDiary AfterFind") if diary.Diary ! = "" { b, err := base64.StdEncoding.DecodeString(diary.Diary) if err == nil { keyname := fmt.Sprintf("user_code_%v", diary.Userid) key, ok := utils.GetCache(keyname) if ! ok { utils.Log.Errorf("GetCache(%s) failed!" , keyname) return errors.New(" Failed to get user key code!" ) } rs, err := utils.AesDeCrypt(b, []byte(utils.MD5(key.(string)))) if err == nil { diary.Diary = string(rs) } else { utils.Log.Errorf("AesDeCrypt(%s) [%d]  failed! %v", diary.Diary, diary.Userid, err) } } else { utils.Log.Errorf("DecodeStringB64(%s) failed! %v", diary.Diary, err) } } return }Copy the code

That’s it! That’s it!

Really, the rest of the code is unchanged, isn’t it great? Just add encryption and decryption to your data model classes, it’s not awesome! Use it quickly and fail

Is that enough?

Of course not!

This is just the primary use of it, I have a more advanced scheme oh!

1. Reflection use with Golang

Notice the tag I defined above?

Diary      string    `json:"diary" enc:"aes"` //diary text
Copy the code

I added an ENC tag, what can this be used for? By reading the tag through reflection, we can define the encryption algorithm, or call decryption on the outer layer of the database, which can delay decryption.

You can decrypt it where you want, not when the data comes back!

Here is the code for reflection

func (diary *UserDiary) DoDec() { t := reflect.TypeOf(diary) for i := 0; i < t.NumField(); i++ { tag := t.Field(i).Tag.Get("enc") if tag ! = "" { b, err := base64.StdEncoding.DecodeString(diary.Diary) if err == nil { keyname := fmt.Sprintf("user_code_%v", diary.Userid) key, ok := utils.GetCache(keyname) if ! ok { utils.Log.Errorf("GetCache(%s) failed!" , keyname) return } rs, err := utils.AesDeCrypt(b, []byte(utils.MD5(key.(string)))) if err == nil { diary.Diary = string(rs) } else { utils.Log.Errorf("AesDeCrypt(%s) [%d]  failed! %v", diary.Diary, diary.Userid, err) } } else { utils.Log.Errorf("DecodeStringB64(%s) failed! %v", diary.Diary, err) } } } }Copy the code

2. What else can YOU do with tag?

  1. Custom function
  2. Data authentication
  3. Automatic backup when data is deleted
  4. Data desensitization

I won’t list more here

Imaginary space

Gorm hooks are no different from Mybatis interceptors, struct tags are no different from Java annotations. Taking a page out of gorM’s playbook, could we also do a wave of AOP programming under Golang?

Have a nice weekend!