shiro - custom realm

In the blogs written before, .ini files are used to obtain information, including user information, role information, permission information, etc. When entering the system, it is read from the .ini file. In practice, unless the system is particularly simple, this is generally not done. These information need to be maintained in the database, so a custom realm is needed.

1. Create a database table

  First, create three new tables in the database: t_user, t_role and t_permission, which store user information, role information and permission information respectively. The table creation statement is as follows:

CREATE TABLE `t_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `rolename` varchar(20) DEFAULT NULL COMMENT 'rolename',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

CREATE TABLE `t_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'User primary key',
  `username` varchar(20) NOT NULL COMMENT 'username',
  `password` varchar(20) NOT NULL COMMENT 'password',
  `role_id` int(11) DEFAULT NULL COMMENT 'Foreign key associated role table',
  PRIMARY KEY (`id`),
  KEY `role_id` (`role_id`),
  CONSTRAINT `t_user_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

CREATE TABLE `t_permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `permissionname` varchar(50) NOT NULL COMMENT 'permission name',
  `role_id` int(11) DEFAULT NULL COMMENT 'Foreign key association role',
  PRIMARY KEY (`id`),
  KEY `role_id` (`role_id`),
  CONSTRAINT `t_permission_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

 I added some test data to each table as follows: 



2. Customize realm

  The database needs to be operated in the custom realm, so you must first write a dao, using the original jdbc, mainly the following custom realm.

public class UserDao {

    //find user by username
    public User getByUsername(Connection conn, String username) throws Exception {

        User resultUser = null;
        String sql = "select * from t_user where username=?";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1, username);
        ResultSet rs = ps.executeQuery();
        if(rs.next()) {
            resultUser = new User();
            resultUser.setId(rs.getInt("id"));
            resultUser.setUsername(rs.getString("username"));
            resultUser.setPassword(rs.getString("password"));
        }
        return resultUser;
    }

    / / Find and change the role owned by the user according to the user name
    public Set<String> getRoles(Connection conn, String username) throws Exception {
        Set<String> roles = new HashSet<String>();
        String sql = "select * from t_user u, t_role r where u.role_id=r.id and u.username=?";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1, username);
        ResultSet rs = ps.executeQuery();
        while(rs.next()) {
            roles.add(rs.getString("rolename"));
        }
        return roles;
    }

    / / Find the permissions owned by the user role based on the user name
    public Set<String> getPerms(Connection conn, String username) throws Exception {
        Set<String> perms = new HashSet<String>();
        String sql = "select * from t_user u, t_role r, t_permission p where u.role_id=r.id and p.role_id=r.id and u.username=?";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1, username);
        ResultSet rs = ps.executeQuery();
        while(rs.next()) {
            perms.add(rs.getString("permissionname"));
        }
        return perms;
    }
}

 With dao, you can write a custom realm next. Custom realm needs to inherit the AuthorizingRealm class, because this class encapsulates many methods, and it is also inherited from the Realm class step by step. After inheriting the AuthorizingRealm class, it needs to be rewritten. Write two methods:

doGetAuthenticationInfo() method: used to authenticate the currently logged in user and obtain authentication information 
doGetAuthorizationInfo() method: used to grant permissions and roles to the currently logged in user (already logged in successfully)

 

Let's take a look at the specific implementation:

public class MyRealm extends AuthorizingRealm {

    private UserDao userDao = new UserDao();

    // Grant permissions and roles to the user who has successfully logged in, and has successfully logged in
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        String username = (String) principals.getPrimaryPrincipal(); //Get username
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        Connection conn = null;
        try {
            conn = DbUtil.getConnection();
            authorizationInfo.setRoles(userDao.getRoles(conn, username)); //Set roles
            authorizationInfo.setStringPermissions(userDao.getPerms(conn, username)); //设置权限            
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace ();
        } finally {
            try {
                DbUtil.closeConnection(conn);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace ();
            }
        }
        return authorizationInfo;
    }

    // Verify the currently logged in user and get the authentication information
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal(); // Get username
        Connection conn = null;
        try {
            conn = DbUtil.getConnection();
            User user = userDao.getByUsername(conn, username); // Only the user information found based on the username, not the password
            if (user != null) {
                AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(
                        user.getUsername(), user.getPassword(), "myrealm");
                return authcInfo;
            } else {
                return null;
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace ();
        } finally {
            try {
                DbUtil.closeConnection(conn);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace ();
            }
        }
        return null;
    }
}

 

 It can be seen from the above two methods: when verifying the identity, the user corresponding to the user name is first found from the database according to the user name entered by the user. At this time, the password is not involved, that is to say, when this step is reached , even if the password entered by the user is incorrect, the user can be found out, and then the correct information of the user is encapsulated in authcInfo and returned to Shiro. The next thing is Shiro, it will be based on the real information here and the user front desk The entered user name and password are verified. At this time, the password is also verified. If the verification is passed, the user will be logged in. Otherwise, it will jump to the specified page. Similarly, when verifying permissions, the roles and permissions related to the user name are first obtained according to the user name, and then encapsulated in authorizationInfo and returned to Shiro.

3. Modify the ini file

  在该配置文件中,[users]和[roles]的信息就可以删掉了,因为这些信息都是从数据库中维护的,另外还要在文件中指定我们自定义的realm的完全限定名,并且指定securityManager的realm使用我们自定义的realm,如下:

[main]
authc.loginUrl=/login
roles.unauthorizedUrl=/unauthorized.jsp
perms.unauthorizedUrl=/unauthorized.jsp

#定义自己的realm
myRealm=demo.shiro.realm.MyRealm
securityManager.realms=$myRealm

#定义请求的地址需要做什么验证
[urls]
/login=anon 
/admin=authc 
/student=roles[teacher]
/teacher=perms["user:create"]

 这样我们自定义的realm就搞定了,根据配置文件,当我们请求…/admin的时候会进行身份认证,所以会进入LoginServlet中,当调用currentUser.login(token);的时候,就会进入我们自定义的realm中的doGetAuthenticationInfo方法进行身份初始化,然后交给Shiro去验证。当我们请求…./student的时候,也会先进行身份验证,就是上面的过程,然后验证通过,当我们再次请求…/student的时候,就会进入我们自定义的realm中的doGetAuthorizationInfo方法进行权限的初始化,然后交给Shiro去验证。 

 

Guess you like

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