shiro的配置主要分为两部分:权限(包括:账户、密码、角色、资源),模块(包括:认证、授权、session、cache、web)。在学习的过程中千万要分开理解,不然很容易混淆。在单独使用shiro的时候,我们需要配置xxx.ini文件来修改我们需要的信息。shiro的每个部分都是以[xxx]开始,其中[main]是模块配置,后面的[users],[roles],[urls]则是权限关配置。
[users] #提供了对用户/密码及其角色的配置,用户名=密码,角色1,角色2 username=password,role1,role2 [roles] #提供了角色及权限之间关系的配置,角色=权限1,权限2 role1=permission1,permission2
[users]:下面配置的是账户、密码,以及该账户的权限。
[roles]:对应每一个角色的访问资源。
(shiro采取的是:一个用户对应多个角色,一个角色对应多个资源)
a)先看一下 配置 的代码:这段代码直接通过源码实现来一步步new出来的。
//全局sercurityManager DefaultSecurityManager securityManager = new DefaultSecurityManager(); //设置authenticator ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator(); authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy()); securityManager.setAuthenticator(authenticator); //设置authorizer ModularRealmAuthorizer authorizer = new ModularRealmAuthorizer(); authorizer.setPermissionResolver(new WildcardPermissionResolver()); securityManager.setAuthorizer(authorizer); //设置Realm DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/shiro"); ds.setUsername("root"); ds.setPassword(""); JdbcRealm jdbcRealm = new JdbcRealm(); jdbcRealm.setDataSource(ds); jdbcRealm.setPermissionsLookupEnabled(true); securityManager.setRealms(Arrays.asList((Realm) jdbcRealm)); //将SecurityManager设置到SecurityUtils 方便全局使用 SecurityUtils.setSecurityManager(securityManager); //登录 Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123"); subject.login(token); Assert.assertTrue(subject.isAuthenticated());
b)介绍:
通过上面这段代码大家可以看见几个主要的实现模块:SecurityManager、Realm等等。这是通过new的方式来实现的,那我们平时的配置又有什么用呢?为了将这些模块组装起来,并且做个一个高可用,低耦合、高扩展性的框架。shiro里面采用了 IOC 的方式,通过JVM的功能实现的java 反射,来组装实现低耦合、高可扩展性。在使用者自己开发的扩展中,通过实现shiro提供的接口完成自己的功能,然后通过配置文件替换默认的功能(shiro的源码很经典,建议大家多读读)。
c)主要模块实现
1、用户认证: 认证realm、AuthenticationStrategy(这个主要、针对多个realm)
认证realm的实现:实现realm接口。这里每一个realm都可以看作是一个元数据查询接口(可以是数据库、txt、redis)。
实现realm: public class AuthenticationTest implements Realm { /** * @see 实现一个认证:每一个realm都会和我们维护的数据联系 */ // 认证 public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("myRealm"); String userName = (String) token.getPrincipal(); // 账户 String password = new String((char[]) token.getCredentials()); // 密码 if (!"userName".equals(userName)) { throw new UnknownAccountException();// 账户错误 } if (!"password".equals(password)) { throw new IncorrectCredentialsException();// 密码错误 } return new SimpleAuthenticationInfo(userName, password, getName()); } // 每个realm都有一个名称 public String getName() { return "myrealm"; } // 是否支持token public boolean supports(AuthenticationToken token) { return token instanceof UsernamePasswordToken; } } 配置:ini ##自定义realm全限名#### myRealm1=com.fxl.Test.LearnOne.shiro.authentication.AuthenticationTest ###注入##### securityManager.realms=$myRealm1
AuthenticationStrategy的类型:当有多个realm的时候,例如有多个数据元,我们就需要知道哪一个正确才能完成认证,也就是认证策略。官方提供了3种,满足我们大部分需要。
FirstSuccessfulStrategy:只要有一个Realm验证成功即可,只返回第一个Realm身份验证成功的认证信息,其他的忽略;
AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和FirstSuccessfulStrategy不同,返回所有Realm身份验证成功的认证信息;(默认)
AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有Realm身份验证成功的认证信息,如果有一个失败就失败了。
修改为所用都成功: allSuccessfulStrategy=org.apache.shiro.authc.pam.AllSuccessfulStrategy securityManager.authenticator.authenticationStrategy=$allSuccessfulStrategy
2、授权:一个subject的权限获取和每一次访问的权限验证,都会使用到authorization模块。
1、默认实现: 默认的资源字符串:资源标识符:操作:对象实例 eg: 单资源单权限:user:update:5 表示的是:用户:修改:角色5 单资源多权限:user:update,create:5 单资源所有权限:user:*:5 authorization的realm实现:继承 authorizingRealm public class Authoriation extends AuthorizingRealm { /*** * @see 权限查询 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { SimpleAuthorizationInfo sai = new SimpleAuthorizationInfo(); // 添加角色 sai.addRole("admim"); // 单角色 List<String> roles = new ArrayList<String>();// 多角色 roles.add("admin1"); roles.add("admin2"); sai.addRoles(roles); // 添加资源权限 sai.addStringPermission("user:update"); List<String> permissions = new ArrayList<String>();// 多角色 permissions.add("user:create"); permissions.add("user:delete"); sai.addStringPermissions(permissions); return sai; } /** * @see 认证查询 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException { // TODO Auto-generated method stub return null; } } 配置ini: ############# authorization ################ myRealm2=com.fxl.Test.LearnOne.shiro.authorization.AuthoriationTest securityManager.realms=$myRealm2 2、自定义的资源字符串实现: 需要实现3个接口:permission,permissionResolver,rolePermissionResolver permission:定义了字符串的 组成方式 和 对比方式 permissionResolver:根据字符串的组成方式来实现不同的permission对象. rolePermissionResolver:通过不同的角色来获取不同的permission对戏。 ps:每一permission都是一个对象实例,所以需要我们实现permission接口,需要我们通过组成方式实现perimission。 eg:permission1: +资源标识符+操作+对象ID permission2: -资源标识符+操作+对象ID 配置的文件(ini): #################自定义的 permission############ authorizer=org.apache.shiro.authz.ModularRealmAuthorizer #自定义permissionResolver permissionResolver=com.fxl.Test.LearnOne.shiro.authorization.MyRolePermissionReslover authorizer.permissionResolver=$permissionResolver #自定义rolePermissionResolver rolePermissionResolver=com.fxl.Test.LearnOne.shiro.authorization.MyPermissionReslover authorizer.rolePermissionResolver=$rolePermissionResolver securityManager.authorizer=$authorizer
3、realm:realm算是authencation和authorization模块的一部分。单独拿出来是因为我们可以通过实现直接继承一个authorizingRealm来实现认证和授权。
public class RealmTest extends AuthorizingRealm { /*** * @see 权限查询 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { SimpleAuthorizationInfo sai = new SimpleAuthorizationInfo(); // 添加角色 sai.addRole("admim"); // 单角色 List<String> roles = new ArrayList<String>();// 多角色 roles.add("admin1"); roles.add("admin2"); sai.addRoles(roles); // 添加资源权限 sai.addStringPermission("user:update"); List<String> permissions = new ArrayList<String>();// 多角色 permissions.add("user:create"); permissions.add("user:delete"); sai.addStringPermissions(permissions); return sai; } /** * @see 认证查询 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("myRealm"); String userName = (String) token.getPrincipal(); // 账户 String password = new String((char[]) token.getCredentials()); // 密码 if (!"userName".equals(userName)) { throw new UnknownAccountException();// 账户错误 } if (!"password".equals(password)) { throw new IncorrectCredentialsException();// 密码错误 } return new SimpleAuthenticationInfo(userName, password, "myRealm"); } } 配置: ############# authorization ################ myRealm3=com.fxl.Test.LearnOne.shiro.authorization.RealmTest securityManager.realms=$myRealm2
3、cache:由于每一次授权、session都会查询一次元数据(例如:从数据库中查询授权字符串)。很影响性能,cache就是我们的不二之选。shiro默认使用的EHchache,当然也可以通过实现接口来实现
缓存主要用在realm查询和session中: 1、扩展接口: cache:shiro不同模块调用的通用接口。如果我需要自己实现cache,则需要将这些接口都换掉 CacheManager:获取一个cache对象。实现这个接口,可以从第三方接口中获取cache。 CacheManagerAware:注入CacheManager,在authencation、authorization、sessionDAO中都有这个接口实现。然后通过IOC,注入我们自己的cache。 2、默认cache:Ehcache 3、配置: realm:认证、授权 myRealm3=com.fxl.Test.LearnOne.shiro.authorization.RealmTest myRealm3.credentialsMatcher=$credentialsMatcher #######开启cacheManager####### myRealm3.cachingEnabled=true myRealm3.authenticationCachingEnabled=true myRealm3.authenticationCacheName=authenticationCache myRealm3.authorizationCachingEnabled=true myRealm3.authorizationCacheName=authorizationCache securityManager.realms=$myRealm3 ##########设置cacheManager################ cacheManager=org.apache.shiro.cache.ehcache.EhCacheManager cacheManager.cacheManagerConfigFile=classpath:shiro-ehcache.xml securityManager.cacheManager=$cacheManager; session配置cache: ##################开启session的cacheManager################# sessionManager=org.apache.shiro.session.mgt.DefaultSessionManager securityManager.sessionManager=$sessionManager; #####################实现sessionDAO的cacheManager########### sessionDAO=com.fxl.Test.LearnOne.shiro.authorization.session.dao.MySessionDAO sessionDAO.activeSessionsCacheName=shiro-activeSessionCache;