本文也是拿来主义,拿别人的代码跑了一遍,算是自己的理解吧
主要两块:
auth-client 客户端(第三方应用)
auth-web(内部依赖auth-server) 服务提供方(这里资源和认证放在一起了,一般认证是独立的)
auth-client的shiro配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- 缓存管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache/ehcache.xml"/>
</bean>
<!-- Realm实现 -->
<bean id="oAuth2Realm" class="com.hjzgg.auth.client.shiro.OAuth2Realm">
<property name="cachingEnabled" value="true"/>
<property name="authenticationCachingEnabled" value="true"/>
<property name="authenticationCacheName" value="authenticationCache"/>
<property name="authorizationCachingEnabled" value="true"/>
<property name="authorizationCacheName" value="authorizationCache"/>
<property name="clientId" value="c1ebe466-1cdc-4bd3-ab69-77c3561b9dee"/>
<property name="clientSecret" value="d8346ea2-6017-43ed-ad68-19c0f971738b"/>
<property name="accessTokenUrl" value="http://127.0.0.1:8080/auth-web/oauth/accessToken"/>
<property name="userInfoUrl" value="http://127.0.0.1:8080/auth-web/oauth/userInfo"/>
<property name="redirectUrl" value="http://127.0.0.1:8080/auth-client/login"/>
</bean>
<!-- 会话ID生成器 -->
<bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
<!-- 会话Cookie模板 -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="sid"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="-1"/>
</bean>
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="2592000"/><!-- 30天 -->
</bean>
<!-- rememberMe管理器 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<!-- rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)-->
<property name="cipherKey"
value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/>
<property name="cookie" ref="rememberMeCookie"/>
</bean>
<!-- 会话DAO -->
<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
<property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
<property name="sessionIdGenerator" ref="sessionIdGenerator"/>
</bean>
<!-- 会话管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1800000"/>
<property name="deleteInvalidSessions" value="true"/>
<property name="sessionValidationSchedulerEnabled" value="true"/>
<property name="sessionDAO" ref="sessionDAO"/>
<property name="sessionIdCookieEnabled" value="true"/>
<property name="sessionIdCookie" ref="sessionIdCookie"/>
</bean>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="oAuth2Realm"/>
<property name="sessionManager" ref="sessionManager"/>
<property name="cacheManager" ref="cacheManager"/>
<property name="rememberMeManager" ref="rememberMeManager"/>
</bean>
<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="securityManager"/>
</bean>
<!-- OAuth2身份验证过滤器 -->
<bean id="oAuth2AuthenticationFilter" class="com.hjzgg.auth.client.shiro.OAuth2AuthenticationFilter">
<property name="authcCodeParam" value="code"/>
<property name="failureUrl" value="/oauth2Failure.jsp"/>
</bean>
<!-- Shiro的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="http://127.0.0.1:8080/auth-web/oauth/authorize?client_id=c1ebe466-1cdc-4bd3-ab69-77c3561b9dee&response_type=code&redirect_uri=http://127.0.0.1:8080/auth-client/login"/>
<property name="successUrl" value="/index.jsp"/>
<property name="filters">
<util:map>
<entry key="oauth2Authc" value-ref="oAuth2AuthenticationFilter"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/oauth2Failure.jsp = anon
/login = oauth2Authc
/logout = logout
/** = user
</value>
</property>
</bean>
<!-- Shiro生命周期处理器-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
</beans>
auth-web的shiro配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- 缓存管理器 -->
<bean id="cacheManager" class="com.hjzgg.auth.util.SpringCacheManagerWrapper">
<property name="cacheManager" ref="springCacheManager"/>
</bean>
<bean id="springCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcacheManager"/>
</bean>
<!--ehcache-->
<bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache/ehcache.xml"/>
</bean>
<!-- 凭证匹配器 -->
<bean id="credentialsMatcher" class="com.hjzgg.auth.shiro.RetryLimitHashedCredentialsMatcher">
<constructor-arg ref="cacheManager"/>
<property name="hashAlgorithmName" value="md5"/>
<property name="hashIterations" value="2"/>
<property name="storedCredentialsHexEncoded" value="true"/>
</bean>
<!-- Realm实现 -->
<bean id="userRealm" class="com.hjzgg.auth.shiro.UserRealm">
<!--<property name="credentialsMatcher" ref="credentialsMatcher"/>-->
<property name="cachingEnabled" value="false"/>
<!--<property name="authenticationCachingEnabled" value="true"/>-->
<!--<property name="authenticationCacheName" value="authenticationCache"/>-->
<!--<property name="authorizationCachingEnabled" value="true"/>-->
<!--<property name="authorizationCacheName" value="authorizationCache"/>-->
</bean>
<!-- 会话ID生成器 -->
<bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
<!-- 会话Cookie模板 -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="sid"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="-1"/>
</bean>
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="2592000"/><!-- 30天 -->
</bean>
<!-- rememberMe管理器 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<!-- rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)-->
<property name="cipherKey"
value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/>
<property name="cookie" ref="rememberMeCookie"/>
</bean>
<!-- 会话DAO -->
<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
<property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
<property name="sessionIdGenerator" ref="sessionIdGenerator"/>
</bean>
<!-- 会话管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1800000"/>
<property name="deleteInvalidSessions" value="true"/>
<property name="sessionValidationSchedulerEnabled" value="true"/>
<property name="sessionDAO" ref="sessionDAO"/>
<property name="sessionIdCookieEnabled" value="true"/>
<property name="sessionIdCookie" ref="sessionIdCookie"/>
</bean>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm"/>
<property name="sessionManager" ref="sessionManager"/>
<property name="cacheManager" ref="cacheManager"/>
<property name="rememberMeManager" ref="rememberMeManager"/>
</bean>
<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="securityManager"/>
</bean>
<bean name="formAuthenticationFilter" class="com.hjzgg.auth.shiro.FormAuthenticationFilter"/>
<!-- Shiro的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="filters">
<util:map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
</util:map>
</property>
<property name="loginUrl" value="/login.jsp"/>
<property name="successUrl" value="/index.jsp"/>
<property name="filterChainDefinitions">
<value>
/logout = logout
/login.jsp = authc
/oauth/authorize=anon
/oauth/accessToken=anon
/oauth/userInfo=anon
/** = roles[admin]
</value>
</property>
</bean>
<!-- Shiro生命周期处理器-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
</beans>
这里区别于常规shiro的主要逻辑是:
1. 将本地shiro的登录认证改为了认证服务器auth-web的地址
2. auth-web执行完回调auth-client提供的redirect_uri
3. auth-client通过redirect_uri被自己配置的oauth2Authc过滤器拦截
4. OAuth2AuthenticationFilter根据从认证服务器auth-web获取到的code进行executeLogin
5. login时是通过Oauth2Realm.doGetAuthenticationIfo进行,内部会调用auth-web生成token的uri进行获取token
6. 后面通过传递token获取auth-web的userInfo,生成authenticationInfo
下面稍微详细展开叙述
1. 访问127.0.0.1:8080/auth-client/
因为auth-client的shiro中配置的loginUrl配置的http://127.0.0.1:8080/auth-web/oauth/authorize*******
则进入到auth-web的authorize
authorize 主要是验证clientID 和 是否登录,因没有登录则跳转到自己的login.jsp
数组账号密码登录
则进入 FormAuthenticationFilter 父类AuthenticatingFilter的executeLogin
subject.login(token)后,会调用自己配置的UserRealm <bean id="userRealm" class="com.hjzgg.auth.shiro.UserRealm">
上面就是常规的shiro认证,完成后则会回调上次访问的url,及authorize
同时在authorize里面生成了code,就是授权码
后面就回调了auth-client传过来的redirectUri http://127.0.0.1:8080/auth-client/login
则被配置的oAuth2AuthenticationFilter拦截 进入父类的 executeLogin
subject.login 后 调用 自身配置的 <bean id="oAuth2Realm" class="com.hjzgg.auth.client.shiro.OAuth2Realm">
内部会调用 auth-web的accessToken获取token
改方法主要是先验证clientId,clientkey,authcode无误后生成token
获取token后,调用auth-web的userinfo 获取用户信息
用获取到的userinfo 进行后续的认证
至此,整个流程完成。
后续需要访问auth-web资源的,把token带上,即可访问。
另注意下 shiro 缓存配置
shiro主要使用AuthenticatingRealm 类进行认证,其中getAuthenticationInfo方法中使用了缓存。
在getAuthenticationInfo中,首先判断是否有缓存记录,没有的话,再调用子类Ream的doGetAuthenticationInfo方法查询数据库,
再将查询到数据缓存起来,下次就不用查询数据库。
<!-- Realm实现 -->
<bean id="userRealm" class="com.hjzgg.auth.shiro.UserRealm">
<!--<property name="credentialsMatcher" ref="credentialsMatcher"/>-->
<property name="cachingEnabled" value="true"/>
<property name="authenticationCachingEnabled" value="true"/>
<property name="authenticationCacheName" value="authenticationCache"/>
<property name="authorizationCachingEnabled" value="true"/>
<property name="authorizationCacheName" value="authorizationCache"/>
</bean>
AuthenticatingRealm
getCachedAuthenticationInfo
getAuthenticationCacheKey
UsernamePasswordToken
看完上面的,也就清晰的知道缓存时通过username区分的,我们也可以根据token 授权码之类的
所用到的源码:http://download.csdn.net/download/stonexmx/10126569