actframework / act-aaa-plugin

Use OSGL aaa library to mange Authentication/Authorization/Accounting for ActFramework Application
Apache License 2.0
6 stars 2 forks source link

Could AAA support multiple login types dynamically #15

Open leeaee opened 7 years ago

leeaee commented 7 years ago

Background In some scenario, we need to support user login with multiple types, such as login by register username, register mobile or some third party SSO account.

Currently You could define which field need AAA checked for login by overriding the method protected String userKey(). But once you fixed the value, AAA could not change the checking field.

For example: I have a user in my database as follow, and I want to support user could log in with login_name, mobile, and email. User of database

  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'PK',
  `login_name` VARCHAR(63) NULL COMMENT 'login name',
  `mobile` VARCHAR(31) NULL COMMENT 'login mobile',
  `email` VARCHAR(127) NULL COMMENT 'login email',
  `password` VARCHAR(63) NOT NULL COMMENT 'password in sha1',
  `salt` VARCHAR(31) NOT NULL COMMENT 'salt for password',
  `state` VARCHAR(31) NOT NULL DEFAULT '' COMMENT 'user state',

User entity

@DB()
@Entity
@Table(name = "bb_user")
public class User implements SimpleBean {

    public Long id;

    @Column(name = "login_name", updatable = false)
    public String loginName;

    public String mobile;

    public String email;

    public String password;

    public String salt;

    public String state;
}

Currently, in AAA framework, we could only bind a fixed field. It means we could only support the type of loginName to log in.

public class SecurityService extends ActAAAService.Base<User> {

    public static final int HASH_INTERATIONS = 1024;
    public static final int SALT_SIZE = 8;
    private static final String USER_KEY = "loginName";

    @Override
    protected String userKey() {
        return USER_KEY;
    }
}

If you want to change the login type, you need to modify the USER_KEY. But this could not change dynamically. And there another issue, we could not get the login type from login request to AAA.

Workaround Here I have a workaround for this kind of situation.

  1. Define a loginType class for the field.

    public enum LoginType {
    
    NAME("loginName"),
    MOBILE("mobile"),
    EMAIL("email");
    
    private String field;
    
    LoginType(String field) {
        this.field = field;
    }
    
    public String getField() {
        return field;
    }
    }
  2. Add the login type in login request as fomart: identifier:login-type:login-value
    {
    "identifier":"MOBILE:13881882856",
    "password":"111111"
    }
  3. Parse login type and login value for login method.

    @PostAction("login")
    @ResponseStatus(200)
    public void login(String identifier, String password, ActionContext context) {
        User user = authenticate(identifier, password);
        unauthorizedIf(null == user);
        context.login(identifier);
    }
    
    private User authenticate(String identifier, String password) {
        String[] keyValue = identifier.split(SecurityService.SPILT_CHAR);
        User user = dao.findOneBy(LoginType.valueOf(keyValue[0]).getField(), keyValue[1]);
        if (null == user || !isMatchedPassword(user, password)) return null;
        return user;
    }
  4. Overriding findByName in AAA service and also parse the login type and login value from the session username.

    public static final String SPILT_CHAR = ":";
    
    @Override
    public Principal findByName(String name) {
        String[] keyValue = name.split(SPILT_CHAR);
        User user = dao.findOneBy(LoginType.valueOf(keyValue[0]).getField(), keyValue[1]);
        return null == user ? null : principalOf(user);
    }

    @greenlaw110 do you have any other better solution, or could you plan enhancement AAA to support this kind of requirement.