跟我学shiro(开涛)阅读笔记

1、shiro工作方式

首先,我们从外部来看Shiro吧,即从应用程序角度的来观察如何使用Shiro完成工作。如下图:

 

可以看到:应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject;其每个API的含义:

Subject主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;

SecurityManager安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;

Realm域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

 

也就是说对于我们而言,最简单的一个Shiro应用:

1、应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager;

2、我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断。

 

从以上也可以看出,Shiro不提供维护用户/权限,而是通过Realm让开发人员自己注入。

2、shiro

认证:

1、收集用户身份/凭证AuthenticationToken,即如用户名/密码;

2、SecurityUtils.getSubject()获取subject后,调用Subject.login进行登录,如果失败将得到相应的AuthenticationException异常,根据异常提示用户错误信息;否则登录成功;

3、最后调用Subject.logout进行退出操作。

4、前提,(1)配置好SecurityManager并设置给SubjectUtils;(2)reaml安全数据源配置

身份认证流程:

流程如下:

1、首先调用Subject.login(token)进行登录,其会自动委托给Security Manager,调用之前必须通过SecurityUtils. setSecurityManager()设置

2、SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;

3、Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现

4、Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;

5、Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。

realm:DataSource—ca

org.apache.shiro.realm.Realm接口如下: 

String getName(); //返回一个唯一的Realm名字  
boolean supports(AuthenticationToken token); //判断此Realm是否支持此Token  
AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;  //根据Token获取认证信息  

 getAuthenticationInfo()的作用是:(1)判断是否是安全数据,不是抛异常(2)new simpleAuthenticationInfo并return [AuthenticatingRealm中封装了方法优先 从缓存中获取 authenticationInfo]

[注:tx框架中在获取user中完成(1),并override了获取authenticationInfo]

4、SecurityManager:

【获取1】

    //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);  

【获取2】

     /**
     * 安全管理器
     *
     * @return
     */
    @Bean("securityManager")
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(shiroRealm());
        securityManager.setSessionManager(sessionManager());
        securityManager.setCacheManager(cacheManager());
        return securityManager;
    }

    //advisor.setSecurityManager(securityManager());   

SecurityManager(类DefaultWebSecurityManager)

(1)为其设置authenticator 、authorizer 、Realm 、SessionManager、CacheManager

(2)将 SecurityManager 设置到 SecurityUtils 方便全局使用 

Authenticator & AuthenticationStratrgy

shiro默认提供三种认证策略

3、shiro—授权

授权方式

Shiro支持三种方式的授权:

编程式:通过写if/else授权代码块完成: 

 

Subject subject = SecurityUtils.getSubject();  
if(subject.hasRole(“admin”)) {  
    //有权限  
} else {  
    //无权限  
}   

 

注解式:通过在执行的Java方法上放置相应的注解完成: 

 

@RequiresRoles("admin")  
public void hello() {  
    //有权限  
}   

  没有权限将抛出相应的异常;

 

JSP/GSP标签:在JSP/GSP页面通过相应的标签完成: 

<shiro:hasRole name="admin">  
<!— 有权限 —>  
</shiro:hasRole>   

判断权限

Shiro提供了

isPermitted和isPermittedAll用于判断用户是否拥有某个权限或所有权限。

checkPermission("")  checkPermissions("") 无权限时抛异常

授权流程

1、首先调用Subject.isPermitted*/hasRole*接口,其会委托给SecurityManager,而SecurityManager接着会委托给Authorizer;

2、Authorizer是真正的授权者,如果我们调用如isPermitted(“user:view”),其首先会通过PermissionResolver把字符串转换成相应的Permission实例

3、在进行授权之前,其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限;

4、Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个Realm,会委托给ModularRealmAuthorizer进行循环判断,如果匹配如isPermitted*/hasRole*会返回true,否则返回false表示授权失败。

[3]

    /**
     * 权限
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("za方法");
        String username = (String) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setStringPermissions(adminService.permissions(username));
        return authorizationInfo;
    }

 

 

ModularRealmAuthorizer进行多Realm匹配流程:

1、首先检查相应的Realm是否实现了实现了Authorizer;

2、如果实现了Authorizer,那么接着调用其相应的isPermitted*/hasRole*接口进行匹配;

3、如果有一个Realm匹配那么将返回true,否则返回false。

 

如果Realm进行授权的话,应该继承AuthorizingRealm,其流程是:

1.1、如果调用hasRole*,则直接获取AuthorizationInfo.getRoles()与传入的角色比较即可;

1.2、首先如果调用如isPermitted(“user:view”),首先通过PermissionResolver将权限字符串转换成相应的Permission实例,默认使用WildcardPermissionResolver,即转换为通配符的WildcardPermission;

2、通过AuthorizationInfo.getObjectPermissions()得到Permission实例集合;通过AuthorizationInfo. getStringPermissions()得到字符串集合并通过PermissionResolver解析为Permission实例;然后获取用户的角色,并通过RolePermissionResolver解析角色对应的权限集合(默认没有实现,可以自己提供);

3、接着调用Permission. implies(Permission p)逐个与传入的权限比较,如果有匹配的则返回true,否则false。 

自定义realm:

public class RcRealm extends AuthorizingRealm {

    @Autowired
    private AdminService adminService;

    /**
     * 认证
     * 获取身份验证信息
     * @param token
     * @return
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
        System.out.println("ca方法");
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo
                (token.getPrincipal(), token.getCredentials(), getName());
        return simpleAuthenticationInfo;
    }

    /**
     * 权限
     * 根据用户身份 获取授权信息
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("za方法");
        String username = (String) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setStringPermissions(adminService.permissions(username));
        return authorizationInfo;
    }

}

public class UserRealm extends AuthorizingRealm {     
    private UserService userService = new UserServiceImpl();     

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {                 
         String username = (String)principals.getPrimaryPrincipal();         
         SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();         
         authorizationInfo.setRoles(userService.findRoles(username));             
         authorizationInfo.setStringPermissions(userService.findPermissions(username));         
         return authorizationInfo;     
    }     
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {         
         String username = (String)token.getPrincipal();         
         User user = userService.findByUsername(username);         
         if(user == null) {             
            throw new UnknownAccountException();//没找到帐号         
         }         
         if(Boolean.TRUE.equals(user.getLocked())) {             
            throw new LockedAccountException(); //帐号锁定         
         }         
        //交给 AuthenticatingRealm 使用 CredentialsMatcher 进行密码匹配,如果觉得人家 的不好可以在此判断或自定义实现         
         SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(                                 
                    user.getUsername(), //用户名                 
                    user.getPassword(), //密码                 
                    ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt                                 
                    getName()  //realm name         
                );         
         return authenticationInfo;     
    }
 } 

另外我们可以使用 JdbcRealm,需要做的操作如下:

1、执行 sql/ shiro-init-data.sql 插入相关的权限数据;

2、使用 shiro-jdbc-authorizer.ini 配置文件,需要设置 jdbcRealm.permissionsLookupEnabled 为 true 来开启权限查询。 

5、加密

【base64】

String str = "hello"; 
String base64Encoded = Base64.encodeToString(str.getBytes());
String str2 = Base64.decodeToString(base64Encoded);
Assert.assertEquals(str, str2); 

【16进制】

String str = "hello"; 
String base64Encoded = Hex.encodeToString(str.getBytes()); 
String str2 = new String(Hex.decode(base64Encoded.getBytes())); 
Assert.assertEquals(str, str2); 

Md5

String str = "hello"; 
String md5Encoded= DigestUtils.md5Hex(str); 
String str = "hello"; 
String salt = "123"; 
String md5 = new Md5Hash(str, salt).toString();//还可以转换为 toBase64()/toHex() 

SHA

String str = "hello"; 
String salt = "123"; 
String sha1 = new Sha256Hash(str, salt).toString(); 

【通用散列支持】

String str = "hello";
 String salt = "123"; 
//内部使用 MessageDigest 
String simpleHash = new SimpleHash("SHA-1", str, salt).toString(); 

[注]

类 CodecSupport,提供了 toBytes(str, "utf-8") / toString(bytes, "utf-8")用于在 byte 数组/String 之间转换

=====================================

Q2:

DigestUtils.md5Hex()如何加盐?底层实现

Q1:

官网用shiro(只认证无授权),光大用shiro(认证授权),其他不用shiro,各自原因? 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. spring 1.1 【第二章】 IoC 之 2.3 IoC的配置使用——跟我学Spring3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.2 【第二章】 IoC 之 2.1 IoC基础 ——跟我学Spring3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.3 【第二章】 IoC 之 2.2 IoC 容器基本原理 ——跟我学Spring3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 1.4 【第三章】 DI 之 3.1 DI的配置使用 ——跟我学spring3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 1.5 【第三章】 DI 之 3.2 循环依赖 ——跟我学spring3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 1.6 【第三章】 DI 之 3.1 DI的配置使用 ——跟我学spring3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 1.7 【第三章】 DI 之 3.2 循环依赖 ——跟我学spring3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 1.8 【第三章】 DI 之 3.3 更多DI的知识 ——跟我学spring3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .104 1.9 【第三章】 DI 之 3.4 Bean的作用域 ——跟我学spring3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .121 1.10 »Spring 之AOP AspectJ切入点语法详解(最全了,不需要再去其他地找了) . . . . . . . . . . . . . .132 1.11 【第四章】 资源 之 4.1 基础知识 ——跟我学spring3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .153 1.12 【第四章】 资源 之 4.2 内置Resource实现 ——跟我学spring3 . . . . . . . . . . . . . . . . . . . . . . . . . .156 1.13 【第四章】 资源 之 4.3 访问Resource ——跟我学spring3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .165 1.14 【第四章】 资源 之 4.4 Resource通配符路径 ——跟我学spring3 . . . . . . . . . . . . . . . . . . . . . . . .171 1.15 【第五章】Spring表达式语言 之 5.1 概述 5.2 SpEL基础 ——跟我学spring3 . . . . . . . . . . . . . . .177 1.16 【第五章】Spring表达式语言 之 5.3 SpEL语法 ——跟我学spring3 . . . . . . . . . . . . . . . . . . . . . .183 1.17 【第五章】Spring表达式语言 之 5.4在Bean定义中使用EL—跟我学spring3 . . . . . . . . . . . . . . . .197 1.18 【第六章】 AOP 之 6.1 AOP基础 ——跟我学spring3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .202 1.19 【第六章】 AOP 之 6.2 AOP的HelloWorld ——跟我学spring3 . . . . . . . . . . . . . . . . . . . . . . . . .2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值