shiro源码分析(四)具体的Realm

首先还是Realm的接口设计图: 

 
这里只来说明SimpleAccountRealm和JdbcRealm的实现。 
首先是SimpleAccountRealm不用关心数据的具体来源,只提供了与上层的交互,即实现了AuthenticatingRealm遗留的AuthenticationInfo doGetAuthenticationInfo和AuthorizingRealm遗留的AuthorizationInfo doGetAuthorizationInfo。 
如下: 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

protected final Map<String, SimpleAccount> users; //username-to-SimpleAccount

    protected final Map<String, SimpleRole> roles; //roleName-to-SimpleRole

    protected final ReadWriteLock USERS_LOCK;

    protected final ReadWriteLock ROLES_LOCK;

 

    public SimpleAccountRealm() {

        this.users = new LinkedHashMap<String, SimpleAccount>();

        this.roles = new LinkedHashMap<String, SimpleRole>();

        USERS_LOCK = new ReentrantReadWriteLock();

        ROLES_LOCK = new ReentrantReadWriteLock();

        //SimpleAccountRealms are memory-only realms - no need for an additional cache mechanism since we're

        //already as memory-efficient as we can be:

        setCachingEnabled(false);

    }

SimpleAccountRealm内部有四个属性,Map<String, SimpleAccount> users:用于存放用户账号信息,Map<String, SimpleRole> roles用于存放角色名的信息。这两个都是各种配置的最终归属存储地。 
ReadWriteLock USERS_LOCK:由于这些配置信息,一般不会去修改,大部分时间用于查询,所以要使用读写锁。一般的synchronized同步,不管你是读还是写,都要进行等待。写与写需要进行同步,写与读也要进行同步,但读与读却并不需要进行同步,所以对于那些经常读的场景,要使用读写锁ReadWriteLock 来提升性能。ReadWriteLock ROLES_LOCK同理。 
有了以上数据源,实现父类的遗留的方法就比较简单了。如下: 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        UsernamePasswordToken upToken = (UsernamePasswordToken) token;

        SimpleAccount account = getUser(upToken.getUsername());

 

        if (account != null) {

 

            if (account.isLocked()) {

                throw new LockedAccountException("Account [" + account + "] is locked.");

            }

            if (account.isCredentialsExpired()) {

                String msg = "The credentials for account [" + account + "] are expired";

                throw new ExpiredCredentialsException(msg);

            }

 

        }

 

        return account;

    }

 

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        String username = getUsername(principals);

        USERS_LOCK.readLock().lock();

        try {

            return this.users.get(username);

        } finally {

            USERS_LOCK.readLock().unlock();

        }

    }

代码就很简单了,就是从users中取出相应的用户数据。接下来要分析清几个概念: 
AuthorizationInfo、AuthenticationInfo、SimpleAccount、SimpleRole、PrincipalCollection。 
PrincipalCollection:看下文档介绍 

引用

/** 
* A collection of all principals associated with a corresponding { @link Subject Subject}.  A <em>principal</em> is 
* just a security term for an identifying attribute, such as a username or user id or social security number or 
* anything else that can be considered an 'identifying' attribute for a { @code Subject}. 
* <p/> 
* A PrincipalCollection organizes its internal principals based on the { @code Realm} where they came from when the 
* Subject was first created.  To obtain the principal(s) for a specific Realm, see the { @link #fromRealm} method.  You 
* can also see which realms contributed to this collection via the { @link #getRealmNames() getRealmNames()} method. 
*/ 

一个principal仅仅是Subject的一个标识而已,如可以是用户名,用户id等。PrincipalCollection则是这些属性的集合。每个用户属性可以来自不同的Realm。Collection fromRealm(String realmName)可以获取某个Realm的所有用户属性。Set<String> getRealmNames()可以获取到与Subject关联的用户的属性来自于哪些Realm。 
Object getPrimaryPrincipal():主要是用于获取唯一标示,如UUID、username等。 
接口如下: 

 
MutablePrincipalCollection如下: 

?


1

2

3

4

5

6

public interface MutablePrincipalCollection extends PrincipalCollection {

    void add(Object principal, String realmName);

    void addAll(Collection principals, String realmName);

    void addAll(PrincipalCollection principals);

    void clear();

}

我们知道每一个标示都有所属的realm,所以再添加的时候,要带上realmName。 
SimplePrincipalCollection: 

?


1

private Map<String, Set> realmPrincipals;

一个重要的数据集合,key是realm的name,value是principal集合。 
这个接口分支一直在强调,每个principal都是有所属的realm的。 
PrincipalMap:我这一块没有搞明白,先放下。 

AuthenticationInfo 它是含有用户和密码信息的地方: 

?


1

2

3

4

public interface AuthenticationInfo extends Serializable {

    PrincipalCollection getPrincipals();

    Object getCredentials();

}

AuthorizationInfo :存放用户权限的地方 

?


1

2

3

4

5

public interface AuthorizationInfo extends Serializable {

    Collection<String> getRoles();

    Collection<String> getStringPermissions();

    Collection<Permission> getObjectPermissions();

}

类图如下: 
 
MergableAuthenticationInfo 意味着AuthenticationInfo可以进行合并: 

?


1

2

3

public interface MergableAuthenticationInfo extends AuthenticationInfo {

    void merge(AuthenticationInfo info);

}

SaltedAuthenticationInfo 主要用于密码匹配,后续文章专门说明: 

?


1

2

3

public interface SaltedAuthenticationInfo extends AuthenticationInfo {

    ByteSource getCredentialsSalt();

}

SimpleAuthenticationInfo:存储了三个重要的属性: 

?


1

2

3

protected PrincipalCollection principals;

    protected Object credentials;

    protected ByteSource credentialsSalt;

然后就是实现了MergableAuthenticationInfo 接口,可以进行合并,这里的合并在第一篇文章中Realm认证中提到过: 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

public void merge(AuthenticationInfo info) {

        if (info == null || info.getPrincipals() == null || info.getPrincipals().isEmpty()) {

            return;

        }

 

        if (this.principals == null) {

            this.principals = info.getPrincipals();

        } else {

            if (!(this.principals instanceof MutablePrincipalCollection)) {

                this.principals = new SimplePrincipalCollection(this.principals);

            }

            ((MutablePrincipalCollection) this.principals).addAll(info.getPrincipals());

        }

 

        //only mess with a salt value if we don't have one yet.  It doesn't make sense

        //to merge salt values from different realms because a salt is used only within

        //the realm's credential matching process.  But if the current instance's salt

        //is null, then it can't hurt to pull in a non-null value if one exists.

        //

        //since 1.1:

        if (this.credentialsSalt == null && info instanceof SaltedAuthenticationInfo) {

            this.credentialsSalt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();

        }

 

        Object thisCredentials = getCredentials();

        Object otherCredentials = info.getCredentials();

 

        if (otherCredentials == null) {

            return;

        }

 

        if (thisCredentials == null) {

            this.credentials = otherCredentials;

            return;

        }

 

        if (!(thisCredentials instanceof Collection)) {

            Set newSet = new HashSet();

            newSet.add(thisCredentials);

            setCredentials(newSet);

        }

 

        // At this point, the credentials should be a collection

        Collection credentialCollection = (Collection) getCredentials();

        if (otherCredentials instanceof Collection) {

            credentialCollection.addAll((Collection) otherCredentials);

        } else {

            credentialCollection.add(otherCredentials);

        }

    }

主要分principals、credentialsSalt 和credentials三项的合并,代码也和简单。 
SimpleAuthorizationInfo:存放了认证用户的角色和用户权限。 

?


1

2

3

protected Set<String> roles;

protected Set<String> stringPermissions;

protected Set<Permission> objectPermissions;

最重要的就是SimpleAccount: 

?


1

2

3

4

5

6

public class SimpleAccount implements Account, MergableAuthenticationInfo, SaltedAuthenticationInfo, Serializable {

    private SimpleAuthenticationInfo authcInfo;

    private SimpleAuthorizationInfo authzInfo;

    private boolean locked;

    private boolean credentialsExpired;

}

它有SimpleAuthenticationInfo 、SimpleAuthorizationInfo ,所以是用户认证信息和权限信息的汇总。 
还有两个属性locked和credentialsExpired,用来表示用户的锁定和密码过期的状态。 
至此整个SimpleAccount便介绍完了。 

回到SimpleAccountRealm,SimpleAccountRealm已经拥有Map<String, SimpleAccount> users和Map<String, SimpleRole> roles数据了,但是这些数据是怎么产生的呢?这就需要交给它的子类TextConfigurationRealm来完成: 

?


1

2

private volatile String userDefinitions;

private volatile String roleDefinitions;

仅仅两个字符串包含了所有的用户和角色的配置总来源。所以TextConfigurationRealm主要就是对这两个字符串的解析: 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

@Override

protected void onInit() {

        super.onInit();

        processDefinitions();

    }

protected void processDefinitions() {

        try {

          //解析角色配置

            processRoleDefinitions();

         //解析用户配置

            processUserDefinitions();

        } catch (ParseException e) {

            String msg = "Unable to parse user and/or role definitions.";

            throw new ConfigurationException(msg, e);

        }

    }

protected void processRoleDefinitions() throws ParseException {

        String roleDefinitions = getRoleDefinitions();

        if (roleDefinitions == null) {

            return;

        }

        //先将角色字符串按行分割,然后每行再按照key value分割

        Map<String, String> roleDefs = toMap(toLines(roleDefinitions));

        processRoleDefinitions(roleDefs);

    }

protected void processRoleDefinitions(Map<String, String> roleDefs) {

        if (roleDefs == null || roleDefs.isEmpty()) {

            return;

        }

        for (String rolename : roleDefs.keySet()) {

            String value = roleDefs.get(rolename);

 

            SimpleRole role = getRole(rolename);

            if (role == null) {

                role = new SimpleRole(rolename);

                add(role);

            }

 

            Set<Permission> permissions = PermissionUtils.resolveDelimitedPermissions(value, getPermissionResolver());

            role.setPermissions(permissions);

        }

    }

再通过PermissionResolver将字符串形式的权限转化成Permission对象,知道大致情况了,就可以了,不需要每一步都弄清楚。 
TextConfigurationRealm主要用于解析两个配置字符串,这两个配置字符串的产生则继续交给子类来完成。IniRealm则是通过ini配置文件来产生这两个字符串,PropertiesRealm则是通过properties文件来产生这两个字符串。 

至此,SimpleAccountRealm这一路就大致走通了,接下来就是另一条路JdbcRealm了。 

?


1

2

3

4

5

6

7

public class JdbcRealm extends AuthorizingRealm {

    protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?";

    protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?";

    protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?";

    protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?";

    protected DataSource dataSource;

}

首先是含有这几个默认的sql和DataSource dataSource,用于从数据库中获取相应的用户、角色、权限等数据。 
根据上一篇文章我们知道JdbcRealm 要实现AuthenticatingRealm遗留的AuthenticationInfo doGetAuthenticationInfo和AuthorizingRealm遗留的AuthorizationInfo doGetAuthorizationInfo。下面就看看是怎么来实现的: 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

 

        UsernamePasswordToken upToken = (UsernamePasswordToken) token;

        String username = upToken.getUsername();

 

        // Null username is invalid

        if (username == null) {

            throw new AccountException("Null usernames are not allowed by this realm.");

        }

 

        Connection conn = null;

        SimpleAuthenticationInfo info = null;

        try {

            conn = dataSource.getConnection();

 

            String password = null;

            String salt = null;

            switch (saltStyle) {

            case NO_SALT:

                //根据用户名去查找密码

                password = getPasswordForUser(conn, username)[0];

                break;

            case CRYPT:

                // TODO: separate password and hash from getPasswordForUser[0]

                throw new ConfigurationException("Not implemented yet");

                //break;

            case COLUMN:

                String[] queryResults = getPasswordForUser(conn, username);

                password = queryResults[0];

                salt = queryResults[1];

                break;

            case EXTERNAL:

                password = getPasswordForUser(conn, username)[0];

                 //此时salt不存在数据库中,默认的值为username

                salt = getSaltForUser(username);

            }

 

            if (password == null) {

                throw new UnknownAccountException("No account found for user [" + username + "]");

            }

            //根据用户名、密码、盐值构建一个SimpleAuthenticationInfo

            info = new SimpleAuthenticationInfo(username, password.toCharArray(), getName());

             

            if (salt != null) {

                info.setCredentialsSalt(ByteSource.Util.bytes(salt));

            }

 

        } catch (SQLException e) {

            final String message = "There was a SQL error while authenticating user [" + username + "]";

            if (log.isErrorEnabled()) {

                log.error(message, e);

            }

 

            // Rethrow any SQL errors as an authentication exception

            throw new AuthenticationException(message, e);

        } finally {

            JdbcUtils.closeConnection(conn);

        }

 

        return info;

    }

private String[] getPasswordForUser(Connection conn, String username) throws SQLException {

 

        String[] result;

        boolean returningSeparatedSalt = false;

        switch (saltStyle) {

        case NO_SALT:

        case CRYPT:

        case EXTERNAL:

            result = new String[1];

            break;

        default:

            result = new String[2];

            returningSeparatedSalt = true;

        }

         

        PreparedStatement ps = null;

        ResultSet rs = null;

        try {

            ps = conn.prepareStatement(authenticationQuery);

            ps.setString(1, username);

 

            // Execute query

            rs = ps.executeQuery();

 

            // Loop over results - although we are only expecting one result, since usernames should be unique

            boolean foundResult = false;

            while (rs.next()) {

 

                // Check to ensure only one row is processed

                if (foundResult) {

                    throw new AuthenticationException("More than one user row found for user [" + username + "]. Usernames must be unique.");

                }

 

                result[0] = rs.getString(1);

                if (returningSeparatedSalt) {

                    result[1] = rs.getString(2);

                }

 

                foundResult = true;

            }

        } finally {

            JdbcUtils.closeResultSet(rs);

            JdbcUtils.closeStatement(ps);

        }

 

        return result;

    }

protected String getSaltForUser(String username) {

        return username;

    }

代码很简单就不再一一细说。再看下doGetAuthorizationInfo是怎么实现的: 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

 

        //null usernames are invalid

        if (principals == null) {

            throw new AuthorizationException("PrincipalCollection method argument cannot be null.");

        }

 

        String username = (String) getAvailablePrincipal(principals);

 

        Connection conn = null;

        Set<String> roleNames = null;

        Set<String> permissions = null;

        try {

            conn = dataSource.getConnection();

 

            // Retrieve roles and permissions from database

            roleNames = getRoleNamesForUser(conn, username);

            if (permissionsLookupEnabled) {

                permissions = getPermissions(conn, username, roleNames);

            }

 

        } catch (SQLException e) {

            final String message = "There was a SQL error while authorizing user [" + username + "]";

            if (log.isErrorEnabled()) {

                log.error(message, e);

            }

 

            // Rethrow any SQL errors as an authorization exception

            throw new AuthorizationException(message, e);

        } finally {

            JdbcUtils.closeConnection(conn);

        }

 

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);

        info.setStringPermissions(permissions);

        return info;

 

    }

第一步先根据PrincipalCollection 来获取用户名,第二步根据用户名来获取角色,第三部根据角色和用户名来获取权限。后两步都是执行简单的sql,不再说,看下如何由PrincipalCollection 获取用户名,该方法定义在CachingRealm中: 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

protected Object getAvailablePrincipal(PrincipalCollection principals) {

        Object primary = null;

        if (!CollectionUtils.isEmpty(principals)) {

            Collection thisPrincipals = principals.fromRealm(getName());

            if (!CollectionUtils.isEmpty(thisPrincipals)) {

                primary = thisPrincipals.iterator().next();

            } else {

                //no principals attributed to this particular realm.  Fall back to the 'master' primary:

                primary = principals.getPrimaryPrincipal();

            }

        }

 

        return primary;

    }

两种情况,首先是获取当前Realm的Principals,如果有取其第一个。如果没有,则调用getPrimaryPrincipal()方法。然后看下JdbcRealm的一个简单使用: 
如果默认按照JdbcRealm的sql来作为数据库的查询来说,建表如下: 
users表: 

?


1

2

3

4

5

6

7

8

CREATE TABLE `users` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `username` varchar(45) NOT NULL,

  `password` varchar(45) NOT NULL,

  `password_salt` varchar(45) DEFAULT NULL,

  PRIMARY KEY (`id`),

  UNIQUE KEY `username_UNIQUE` (`username`)

) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

user_roles表: 

?


1

2

3

4

5

6

CREATE TABLE `user_roles` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `username` varchar(45) DEFAULT NULL,

  `role_name` varchar(45) DEFAULT NULL,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

roles_permissions表: 

?


1

2

3

4

5

6

CREATE TABLE `roles_permissions` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `role_name` varchar(45) DEFAULT NULL,

  `permission` varchar(45) DEFAULT NULL,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

文章最后会给出数据库sql文件。 
然后就是配置ini文件: 

?


1

2

3

4

5

6

7

8

9

10

11

[main]

#realm 

dataSource=com.mchange.v2.c3p0.ComboPooledDataSource 

dataSource.driverClass=com.mysql.jdbc.Driver 

dataSource.jdbcUrl=jdbc:mysql://localhost:3306/shiro 

dataSource.user=root 

dataSource.password=XXXXXX

jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm 

jdbcRealm.dataSource=$dataSource 

jdbcRealm.permissionsLookupEnabled=true 

securityManager.realms=$jdbcRealm

使用的dataSource是c3p0的dataSource,mysql驱动也是必然不能少的,所以maven中要加入依赖: 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

<!-- mysql驱动 -->

    <dependency>

        <groupId>mysql</groupId>

        <artifactId>mysql-connector-java</artifactId>

        <version>5.1.29</version>

    </dependency>   

     

    <!-- 连接池 -->

    <dependency>

        <groupId>c3p0</groupId>

        <artifactId>c3p0</artifactId>

        <version>0.9.1.2</version>

    </dependency>

为了输出方便代码更改为: 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

public class ShiroTest {

 

    @Test 

    public void testHelloworld() { 

        init();

         

        Subject subject=login("lg","123");

        System.out.println(subject.hasRole("role1"));

        System.out.println(subject.hasRole("role2"));

        System.out.println(subject.hasRole("role3"));

    }

     

    private Subject login(String userName,String password){

         //3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证) 

        Subject subject = SecurityUtils.getSubject(); 

        UsernamePasswordToken token = new UsernamePasswordToken(userName,password); 

        subject.login(token);

        return subject;

    }

     

    private void init(){

         //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager 

        Factory<org.apache.shiro.mgt.SecurityManager> factory = 

                new IniSecurityManagerFactory("classpath:shiro.ini"); 

        //2、得到SecurityManager实例 并绑定给SecurityUtils 

        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance(); 

        SecurityUtils.setSecurityManager(securityManager); 

    }

}

对于lg用户,在数据库中它是有两个角色的,role1和role2。所以结果为true、true、false。 

?


1

2

3

true

true

false

OK,通过。最后附上JdbcRealm的使用例子。 

时间: 2024-09-29 13:31:10

shiro源码分析(四)具体的Realm的相关文章

shiro源码分析(一)入门

最近闲来无事,准备读个框架源码,经别人推荐shiro,那就准备读读其中的设计.开涛大神已经有了跟我学Shiro系列,那我就跟着这个系列入门然后再深入源代码,所以我的侧重点就是源码分析.  话不多说,上开涛大神的入门案例 地址http://jinnianshilongnian.iteye.com/blog/2019547:  ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Test      public void

JUnit源码分析(四)——从Decorator模式说起

其实我这系列小文,名为源码分析,其实是自己读<设计模式>的读书笔记.Decorator模式在java的IO库中得到应用,java的IO库看起来复杂,其实理解了Decorator模式再回头看可以很好理解并使用.     Decorator模式,也就是装饰器模式,是对象结构型模式之一. 1.意图:动态地给一个对象添加一些额外的职责.给对象添加功能,我们首先想到的是继承,但是如果每增一个功能都需要继承,类的继承体系将无可避免地变的庞大和难以理解.面向对象设计的原则:优先使用组合,而非继承,继承的层次

shiro源码分析(六)CredentialsMatcher 的案例分析

有了上一篇文章的原理分析,这一篇文章主要结合原理来进行使用.  shiro.ini配置为:  ? 1 2 3 4 5 6 7 8 9 10 11 [main] #realm  dataSource=com.mchange.v2.c3p0.ComboPooledDataSource  dataSource.driverClass=com.mysql.jdbc.Driver  dataSource.jdbcUrl=jdbc:mysql://localhost:3306/shiro  dataSour

shiro源码分析(五)CredentialsMatcher

Realm在验证用户身份的时候,要进行密码匹配.最简单的情况就是明文直接匹配,然后就是加密匹配,这里的匹配工作则就是交给CredentialsMatcher来完成的.先看下它的接口方法:  ? 1 2 3 public interface CredentialsMatcher {     boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info); } 根据用户名获取AuthenticationIn

shiro源码分析(三)授权、认证、缓存的接口设计

前两篇文章主要说的是认证过程,这一篇来分析下授权的过程.还是开涛大神的案例(http://jinnianshilongnian.iteye.com/blog/2020017),如下:  ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class ShiroTest {       @Test      public void testHelloworld() {        

shiro源码分析(二)Subject和Session

继续上一篇文章的案例,第一次使用SecurityUtils.getSubject()来获取Subject时  ? 1 2 3 4 5 6 7 8 public static Subject getSubject() {         Subject subject = ThreadContext.getSubject();         if (subject == null) {             subject = (new Subject.Builder()).buildSubj

第二人生的源码分析(四十三)虚拟文件系统线程

由于第二人生是一个3D显示的软件,因此它就需要不断地从服务器下载大量数据,比如纹理图片,不同的角色是使用不同的纹理图片来实现不同的衣服外表的.当显示这些角色时,就使用从服务器下载的纹理图片.如果显示的人物角色比较多,比如有30个人时,这些纹理图片就需要保存到磁盘里.那么怎么样保存到磁盘里呢?保存到磁盘里就需要一个好的文件系统来保存,以及读取数据出来.读写磁盘是一项比较慢的工作,因此需要使用一个线程来实现.还有时读写文件并不需要及时性的动作,可以让线程等到CPU空闲时再去做这些事情.   LLVF

第二人生的源码分析(四十四)虚拟文件系统的请求处理

在虚拟文件系统的消息队列里,主要就是LLVFSThread::Request类的请求,Request类是嵌套类,定义在LLVFSThread类里面.它主要实现对类LLVFS的封装访问,让操作更加方便一些,当然它是继续QueuedRequest类的,这样才可以添加到消息队列里去,否则不能添加到这个消息队列容器,也不能实现请求处理的多态了.   下面是类Request的构造函数. #001 LLVFSThread::Request::Request(handle_t handle, U32 prio

OkHttp 3.7源码分析(四)——缓存策略

OkHttp3.7源码分析文章列表如下: OkHttp源码分析--整体架构 OkHttp源码分析--拦截器 OkHttp源码分析--任务队列 OkHttp源码分析--缓存策略 OkHttp源码分析--多路复用 合理地利用本地缓存可以有效地减少网络开销,减少响应延迟.HTTP报头也定义了很多与缓存有关的域来控制缓存.今天就来讲讲OkHttp中关于缓存部分的实现细节. 1. HTTP缓存策略 首先来了解下HTTP协议中缓存部分的相关域. 1.1 Expires 超时时间,一般用在服务器的respon