shiro

概念:

Subject :任何可以和应用交互的“用户”。

SecurityManager: 相当于SpringMVC中的DispatcherServlet;是shiro的心脏。所有具体交互都通过它进行。它管理着所有Subject,且负责进行认证,授权,会话和缓存管理。

Authenticator:负责Subject的认证。

Authorizer:授权器,控制用户能访问应用中哪些功能。

Realm:一个或者多个realm,可以认为是安全的数据源,用户获取安全实体的。需要自己实现Realm。

SessionManager:管理Session生命周期组件。

CacheManager:缓存控制器,用于管理用户、角色、权限等缓存。这些数据较少改变,放缓存提高访问性能。

Cryptography:密码模块,shiro提供的常见加密组件用于加密、解密。

【shiro认证/登录流程:】

1.获取当前subject,调用SecurityUtils.getSubject();

2.测试当前用户是否已经被认证,即是否已经登录,调用Subject的isAuthenticated()

3.若没有被认证,则把用户名和密码封装为UsernamePasswordToken对象。

4.执行登录,调用Subject的login(token);方法

前4步代码,Controller登录方法中

Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated()) {
    UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassword());
    try {
        subject.login(token);
        //SecurityUtils.getSubject().isPermitted(user.getUserName());
    } catch (AuthenticationException e) {
        return "login";
        }
    }
    return "page/list";
}

5.自定义Realm的方法,从数据库获取对应的记录,返回给shiro


1)继承这个类

org.apache.shiro.realm.AuthorizingRealm;

2)实现方法

doGetAuthenticationInfo

Realm中认证方法:获取UsernamepasswordToken中的用户名,然后从数据库比对,没有用户则抛出异常,也可抛出账户

被锁定异常。有则交给SimpleAuthenticationInfo完成密码比对。

 String userName = (String)token.getPrincipal();
        User user = userService.queryUserByName(userName);
        if (user==null){
            //没有找到账号
            throw new UnknownAccountException();
        }
        Integer status=0;
        if(status.equals(user.getStatus())) {
            //账号锁定
            throw new LockedAccountException();
        }
    /**
     * shiro完成密码的比对,三个参数分别是 :
     * 1.认证的实体信息,可以是username,也可以是数据库对应的实体类对象
     * 2.数据库中的密码
     * 3.盐值
     * 4.当前realm的name,调用父类getName()方法
     *
     */
    SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUserName(),user.getPassword()
    , ByteSource.Util.bytes(user.user.getId()),getName());
        return info;
}

6.由shiro完成密码的比对。

认证相关filter

authcFormAuthenticationFilter基于表单的认证器;如"/**=authc",如果没有登录会跳到登录页面登录
主要属性:
usernameParam:表单提交的用户名参数名(username)
passwordParam:表单提交的密码参数名(password)
rememberMeParam:表单提交的密码参数名(rememberMe)
loginUrl:登录地址(login.jsp)
successUrl:登录成功重定向地址
failureKeyAttribute:登录失败后错误信息存储key(shiroLoginFailure)
authcBasicBasicHttpAuthenticationFilterBasic HTTP身份验证拦截器,主要属性:
applicationName:弹出登录框显示的信息(application)
logoutLogoutFilter退出拦截器,主要属性:redirectUrl:退出成功后重定向的地址(/)示例:
"/logout=logout"
userUserFilter用户拦截器,认证和记住我都可以。示例"/**=user";
anonAnonymousFilter匿名拦截器,即不需要登录也能访问,一般用户静态资源过滤,示例:
"/static/**=anon"

【shiro授权步骤:】

实现方法:

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
1.从PrincipalCollection中获取登录用户的信息

if (principals == null) {
    throw new AuthorizationException("PrincipalCollection方法参数不能为空.");
}

2.利用登录用户信息来获取当前用户的角色或权限(可能需要查询数据库)

3.创建SimpleAuthorizationInfo,并设置roles属性。

4.返回SimpleAuthorizationInfo对象

234代码

String userName = (String)principals.getPrimaryPrincipal();
User user = userService.queryUserByName(userName);
Integer userId=user.getId();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//根据userID获取roles集合
info.setRoles(userRolesService.queryRolesByUser(userId));
//根据userId获取资源permission集合
info.setStringPermissions(resourceService.findMenuByUserId(userId));
return info;

【shiro授权详解:】

授权三种方式:

1.编程式:if...else授权代码块实现

2.注解式:在对应的service或者controller放置相应注解完成,没有权限抛出异常

3.JSP/GSP标签:在页面用标签完成

跟授权相关的filter

rolesRolesAuthroizationFilter角色授权器,验证用户是否拥有所有角色
主要属性:
loginUrl:登录地址(/login.jsp);
unauthorizedUrl:未授权重定向的地址;
示例:"/admin/**=roles[admin]"
permsPermissionsAuthroizationFilter权限授权拦截器,验证用户是否拥有权限,属性和
roels一样;示例:
"/user/**=perms["user:create"]"
portPortFilter端口拦截器,主要属性port(80),示例:"/test=port[80]"
如果用户的访问页面是非80,将重定向到80端口
restHttpMethodPermissionFilterrest风格拦截器,自动根据请求方法创建权限字串
GET=read,Post=create,PUT=update,DELETE=delete,HEAD=read
TRACE=read,OPTIONS=read,MLCOL=create
示例:"/user/=rest[user]",会自动拼出"user:read,user:creat,user:update,
user:delete",权限字符串进行权限匹配(所有都得匹配,isPermittedAll)
sslSslFilterSSL拦截器,只有https请求才通过,否则自动跳转会https端口443;其他和port一样
权限的注解:

@RequiresAuthtication:表示当前Subject已经通过login进行了身份验证,即:Subject.isAuthenticated()返回true

@RequiresUser:表示当前Subject已经身份验证或者通过记住我登录

@RequiresGuest:表示当前Subject没有登录或者记住我,是游客身份

@RequiresRoles(value={"admin","user"},logical=logincal.AND):表示当前Subject需要身份admin和user

@RequiresPermissions(value={"user:a""user:b"},logical=logincal.OR):表示当前Subject需要权限user:a或者user:b

注意点:开发的时候service有可能加@transactional注解,这时候加权限注解会出现错误,应该把注解加到controller层

多Realm授权,只要有一个过就return true.

从数据库中初始化权限和资源:

新建一个类FilterChainDefinitionMapBuilder,以后权限在map中获取,需要注意顺序,和filterChainDefinitions配置一样。

public class FilterChainDefinitionMapBuilder {

    public LinkedHashMap<String,String> buildFilterChainDefinitionMap(){
        LinkedHashMap<String,String> map=new LinkedHashMap<>();
        //TODO 查询数据库
        return map;
    }
spring-shiro.xml中,取消filterChainDefinitions配置,加入了filterChainDefinitionMap 这个property,配置实例工厂,引入

上面新建的类FilterChainDefinitionMapBuilder。

 <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"/>
    
   <!-- <property name="filterChainDefinitions">
        <value>
            /kaptcha.jpg=anon
            /statics/**=anon
            /login.jsp=anon
            /login=anon
            /shiro/logout = logout
            /**=authc
        </value>
    </property>-->
</bean>
<!--配置一个bean,该bean实际上是一个map,通过实例工厂的方式-->
<bean id="filterChainDefinitionMap"
      factory-bean="filterChainDefinitionMapBuilder" factory-method="buildFilterChainDefinitionMap"/>

<bean id="filterChainDefinitionMapBuilder" class="com.queen.manage.shiro.FilterChainDefinitionMapBuilder"></bean>

shiro的加密和密码比对

通过AuthenticatingRealm 的credentialMatcher属性来进行密码比对

1.如何加密字符串:

四个属性分别是:加密方法,加密的字符串,盐,加密次数

SimpleHash simpleHash=new SimpleHash("MD5",user.getPassword(),user.getUserName(),1024);

2.替换当前Realm的credentialMatcher属性,直接使用HashedCredentialMatcher对象,并设置加密算法。

xml配置代码如下:

<bean id="userRealm" class="com.queen.manage.realm.UserRealm">
    <property name="credentialsMatcher">
        <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <property name="hashAlgorithmName" value="MD5"/>
            <property name="hashIterations" value="1024"/>
        </bean>
    </property>
</bean>

配置的作用是自动把前台输入的密码变成MD5加密

【多Realm验证】

spring-shiro.xml中定义多个Realm,通过ModularRealmAuthenticator把多个自定义Realm注入,在securityManager中引入

以前是securityManager直接引入一个Realm,多个Realm中间多了一层

把realms放入securityManager中,便于授权进行。

<bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
   
    <property name="authenticationStrategy">
        <bean class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"/>
    </property>
</bean>
<!-- 安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="authenticator" ref="authenticator"/> 
<property name="realms">
        <list>
            <ref bean="userRealm"/>
            <!--<ref bean="userRealm2"/>-->
        </list>
    </property>
<!--缺少缓存--> </bean>
多Realm下的认证策略:

AuthenticationStrategy接口的默认实现:

FirstSuccessfulStrategy:只要一个realm认证成功即可,只返回第一个成功的realm身份认证信息,其他忽略。

AtLeastOneSuccessfulStrategy:只要一个realm认证成功即可,将返回所有认证成功的realm身份信息。

AllSuccessfulStrategy:所有realm认证成功才算成功,一个错误就失败。

ModularRealmAuthenticator 的默认策略是AtLeastOneSuccessfulStrategy

修改认证策略参照上面。

【shiro会话管理】

shiro的会话管理功能,不依赖底层容器(如web的tomcat),不管javaSE还是javaEE环境都能使用,提供了会话管理,会话事件监听。会话存储/持久化,容器无关的集群,失效/过期支持,对web的透明支持,SSO单点登录支持等特性。

会话相关的API:

 Subject.getSession():获取对话,等价于Subject.getSession(true),如果没有session创建一个,如果Subject.getSession(false),没有session则返回null

 session.getId():获取对话的唯一标识。

 session.getHost();获取当前Subject主机地址。

 session.getTimeOut()&session.setTimeOut(毫秒):获取/设置当前session过期时间。

 session.getStartTimestamp()&session.getLastAccessTime():获取对话的启动时间和最后访问时间,javaSE需要手动调用 

   session.touch()更新最后访问时间,javaEE自动调用

session.touch()&session.stop():更新会话最后访问时间,销毁会话;当subject.logout()时会自动调用stop方法。如果web中

   调用HttpSession.invalidate()也会调用stop销毁会话。

 session.setAttribute(key&value);

session.getAttribute(key);

session.removeAttribute(key);设置、获取,移除会话属性;整个会话范围内都能对属性操作。

注:controller还是建议使用HttpSession,如果要在service层调用会话属性,可以再使用shiro的Session操作

SessionDAO

可以把session写入数据库,然后进行crud的操作。

AbstractSessionDAO:提供SessionDAO基础实现,如生成会话ID等。

CachingSessionDAO:提供了对开发者透明的会话缓存功能,需要设置想要的CacheManager

MemorySessionDAO:直接在内存中进行会话维护

EnterpriseCacheSessionDAO:提供了缓存功能的会话维护,默认情况下使用MapCache实现,内部使用ConcurrentHashMap

保存会话管理。

【会话验证】

shiro提供了会话验证调度器,用于定期检查会话是否过期,如果过期停止会话。

出于性能考虑,一般情况下都是获取会话来验证会话是否过期,但是在web环境中,如果用户不主动退出,是不知道会话是否过期的,

因此要定期检查,shiro提供了会话验证调度器:SessionValidationScheduler

 shiro也提供了使用Quartz会话验证调度器:QuartzSessionValidationScheduler

【缓存】

CacheManagerAware接口

shiro内部相应的组件(DefaultSecurityManager)会自动检测相应对象(如Realm)是否实现了CacheManagerAware

并自动注入相应的CacheManager

Realm缓存

shiro提供了CachingRealm,其实现了CacheManagerAware接口,提供了 缓存的基础实现;AuthenticatingRealm

AuthorizingRealm也分别提供了对AuthenticationInfo和AuthorizationInfo信息的缓存

【remenberMe】

在登录页面选择记住我,是把RemenberMe的cookie写到客户端并保存,关掉页面再打开能访问,某些页面需要认证。

认证和记住我区别:

subject.isAuthenticated():表示用户进行了身份验证登录的,即有subject.login进行登录

subject.isRemembered():表示用户通过记住我登录的,此时可能不是真正的你

二者二选一,即subject.isAuthenticated()==true,则subject.isRemembered()==false;反之一样。

访问建议:

访问一般网页,我们使用user拦截器即可。即认证或者记住我登录的都可以访问

访问一些敏感信息,我们使用authc拦截器,authc会判断用户是否认证登录的,如果不是则返回登录页面。

if (rember!=null) {
    token.setRememberMe(true);
}
如果对rememberMe时长什么有具体要求,可以再xml中调用rememberMeManager具体设置









评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值