Apache Shiro 集成-spring

自己根据shiro 源码捣鼓了一个shiro-spring , 弄上来也给大家看看,其实只是对源码稍加修改,不知道怎么部署源码的同学可以看看,eclipse 项目。

一、在web.xml中添加shiro过滤器

    <!-- Shiro Filter is defined in the spring application context: -->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

二、在applicationContext.xml 加入 shiro 配置,另外配置相关数据库连接

<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ Licensed to the Apache Software Foundation (ASF) under one
  ~ or more contributor license agreements.  See the NOTICE file
  ~ distributed with this work for additional information
  ~ regarding copyright ownership.  The ASF licenses this file
  ~ to you under the Apache License, Version 2.0 (the
  ~ "License"); you may not use this file except in compliance
  ~ with the License.  You may obtain a copy of the License at
  ~
  ~     http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing,
  ~ software distributed under the License is distributed on an
  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  ~ KIND, either express or implied.  See the License for the
  ~ specific language governing permissions and limitations
  ~ under the License.
  -->
<beans xmlns="http://www.springframework.org/schema/beans"
       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-3.0.xsd">

    <!-- Sample RDBMS data source that would exist in any application - not Shiro related. -->
    
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver">
		</property>
		<property name="url"
			value="jdbc:oracle:thin:@localhost:1521:orcl">
		</property>
		<property name="username" value="wforum"></property>
		<property name="password" value="wforum"></property>
	</bean>
    
    <!-- Populates the sample database with sample users and roles. -->
    <bean id="bootstrapDataPopulator" class="org.apache.shiro.samples.spring.BootstrapDataPopulator">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- Simulated business-tier "Manager", not Shiro related, just an example -->
    <bean id="sampleManager" class="org.apache.shiro.samples.spring.DefaultSampleManager"/>

    <!-- =========================================================
         Shiro Core Components - Not Spring Specific
         ========================================================= -->
    <!-- Shiro's main business-tier object for web-enabled applications
         (use DefaultSecurityManager instead when there is no web environment)-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager"/>
        <!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->
        <property name="sessionMode" value="native"/>
        <property name="realm" ref="jdbcRealm"/>
    </bean>

    <!-- Let's use some enterprise caching support for better performance.  You can replace this with any enterprise
         caching framework implementation that you like (Terracotta+Ehcache, Coherence, GigaSpaces, etc -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <!-- Set a net.sf.ehcache.CacheManager instance here if you already have one.  If not, a new one
             will be creaed with a default config:
             <property name="cacheManager" ref="ehCacheManager"/> -->
        <!-- If you don't have a pre-built net.sf.ehcache.CacheManager instance to inject, but you want
             a specific Ehcache configuration to be used, specify that here.  If you don't, a default
             will be used.:
        <property name="cacheManagerConfigFile" value="classpath:some/path/to/ehcache.xml"/> -->
    </bean>

    <!-- Used by the SecurityManager to access security data (users, roles, etc).
         Many other realm implementations can be used too (PropertiesRealm,
         LdapRealm, etc. -->
    <bean id="jdbcRealm" class="org.apache.shiro.samples.spring.realm.SaltAwareJdbcRealm">
        <property name="name" value="jdbcRealm"/>
        <property name="dataSource" ref="dataSource"/>
        <property name="credentialsMatcher">
            <!-- The 'bootstrapDataPopulator' Sha256 hashes the password
                 (using the username as the salt) then base64 encodes it: -->
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <property name="hashAlgorithmName" value="SHA-256"/>
                <!-- true means hex encoded, false means base64 encoded -->
                <property name="storedCredentialsHexEncoded" value="false"/>
            </bean>
        </property>
    </bean>

    <!-- =========================================================
         Shiro Spring-specific integration
         ========================================================= -->
    <!-- Post processor that automatically invokes init() and destroy() methods
         for Spring-configured Shiro objects so you don't have to
         1) specify an init-method and destroy-method attributes for every bean
            definition and
         2) even know which Shiro objects require these methods to be
            called. -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!-- Enable Shiro Annotations for Spring-configured beans.  Only run after
         the lifecycleBeanProcessor has run: -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

    <!-- Secure Spring remoting:  Ensure any Spring Remoting method invocations can be associated
         with a Subject for security checks. -->
    <bean id="secureRemoteInvocationExecutor" class="org.apache.shiro.spring.remoting.SecureRemoteInvocationExecutor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

    <!-- Define the Shiro Filter here (as a FactoryBean) instead of directly in web.xml -
         web.xml uses the DelegatingFilterProxy to access this bean.  This allows us
         to wire things with more control as well utilize nice Spring things such as
         PropertiesPlaceholderConfigurer and abstract beans or anything else we might need: -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/s/login"/>
        <property name="successUrl" value="/s/index"/>
        <property name="unauthorizedUrl" value="/s/unauthorized"/>
        <!-- The 'filters' property is not necessary since any declared javax.servlet.Filter bean
             defined will be automatically acquired and available via its beanName in chain
             definitions, but you can perform overrides or parent/child consolidated configuration
             here if you like: -->
        <!-- <property name="filters">
            <util:map>
                <entry key="aName" value-ref="someFilterPojo"/>
            </util:map>
        </property> -->
        <property name="filterChainDefinitions">
            <value>
                /favicon.ico = anon
                /logo.png = anon
                /shiro.css = anon
                /s/login = anon
                # allow WebStart to pull the jars for the swing app:
                /*.jar = anon
                # everything else requires authentication:
                /** = authc
            </value>
        </property>
    </bean>

</beans>

三、实现JdbcRealm 重写认证方法,另外好像有几种不同Realm ,没细研究,有研究的同学可以补充下。

public class SaltAwareJdbcRealm extends JdbcRealm {

    private static final Logger log = LoggerFactory.getLogger(SaltAwareJdbcRealm.class);

    @Override
    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;
        AuthenticationInfo info = null;
        try {
            conn = dataSource.getConnection();

            String password = getPasswordForUser(conn, username);

            if (password == null) {
                throw new UnknownAccountException("No account found for user [" + username + "]");
            }

            SimpleAuthenticationInfo saInfo = new SimpleAuthenticationInfo(username, password, getName());
            /**
             * This (very bad) example uses the username as the salt in this sample app.  DON'T DO THIS IN A REAL APP!
             *
             * Salts should not be based on anything that a user could enter (attackers can exploit this).  Instead
             * they should ideally be cryptographically-strong randomly generated numbers.
             */
            saInfo.setCredentialsSalt(ByteSource.Util.bytes(username));

            info = saInfo;

        } 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 {

        PreparedStatement ps = null;
        ResultSet rs = null;
        String password = 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.");
                }

                password = rs.getString(1);

                foundResult = true;
            }
        } finally {
            JdbcUtils.closeResultSet(rs);
            JdbcUtils.closeStatement(ps);
        }

        return password;
    }

}

四、剩下的就是登录调用啦。

public class LoginController extends SimpleFormController {

    private static transient final Logger log = LoggerFactory.getLogger(LoginController.class);

    protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object cmd, BindException errors) throws Exception {

        LoginCommand command = (LoginCommand) cmd;

        UsernamePasswordToken token = new UsernamePasswordToken(command.getUsername(), command.getPassword());

        try {
            SecurityUtils.getSubject().login(token);
        } catch (AuthenticationException e) {
            log.debug("Error authenticating.", e);
            errors.reject("error.invalidLogin", "The username or password was not correct.");
        }

        if (errors.hasErrors()) {
            return showForm(request, response, errors);
        } else {
            return new ModelAndView(getSuccessView());
        }
    }
}

五、另外这个例子中还有一个通过Session来保存值的功能,通过key,value的方式来存的,登录之后大家可以看到。代码就不贴了,说白了就没什么意思了,大家自己看吧。

六、关于初始化数据

程序启动时有初始化数据代码,但是好像有点问题,而且没有判断是否存在,重启就报错了,在这里我就把SQL贴出来。

create table users ( username varchar(255) primary key, password varchar(255) not null);
create table roles ( role_name varchar(255) primary key);
create table user_roles ( username varchar(255) not null, role_name varchar(255) not null, constraint user_roles_uq unique ( username, role_name));
create table roles_permissions ( role_name varchar(255) not null, permission varchar(255) not null, primary key (role_name, permission));


插入数据方法在 org.apache.shiro.samples.spring.BootstrapDataPopulator.afterPropertiesSet() 
已经被我屏了,取消屏蔽,再启动 TOMCAT 之后。然后再屏掉吧。不然重启要出错。

 

 

关于包什么的,我截个图,大家自己导,难找的包我已经附上了。

 

猜你喜欢

转载自jianjunlv.iteye.com/blog/1912792