apache shiro整合spring(一)

apache shiro整合spring

maven引入jar包

<dependency>
     <groupId>org.apache.shiro</groupId>
     <artifactId>shiro-all</artifactId>
     <version>1.2.3</version>
</dependency>

 

将shiro配置文件整合到spring体系中

 

方式一:直接在spring的配置文件中import shiro的配置文件

web.xml中配置spring

<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

 

 

在spring配置文件中引入shiro配置文件

<import resource="applicationContext-shiro.xml"/>

 

 

方式二:直接在web.xml中配置shiro的配置文件

<!-- Spring ApplicationContext 配置文件的路径,可使用通配符,多个路径用,号分隔 此参数用于后面的Spring Context Loader -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:/applicationContext-shiro.xml</param-value>
</context-param>
<listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

 

 

在web.xml中定义shiro的过滤器

    <!-- 这里的filter-name 要和spring 的applicationContext-shiro.xml 里的
org.apache.shiro.spring.web.ShiroFilterFactoryBean 的bean name 相同 -->
    <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>

 

 

1.shiro中定义的filtername bean是要被wen.xml引用的,所以这里的filtername要和shiro中的filtername相同 
2.shiro的filter要定义在Struts2的filter之前,否则action无法创建

shiro.xml配置文件(配置校验的策略,哪些校验,哪些放行)

    <!-- 安全管理 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->
        <property name="realm" ref="authRealm"/><!-- 引用自定义的realm -->
        <!-- 二级缓存 -->
        <property name="cacheManager" ref="shiroEhcacheManager"/>
    </bean>

    <!-- 自定义权限认证 -->
    <bean id="authRealm" class="com.fyh.www.shiro.AuthRealm">
<!--        <property name="userService" ref="userService"/> -->
        <!-- 自定义密码加密算法  -->
        <property name="credentialsMatcher" ref="passwordMatcher"/>
    </bean>

    <!-- 设置密码加密策略 md5hash -->
    <bean id="passwordMatcher" class="com.fyh.www.shiro.CustomCredentialsMatcher"/>

    <!-- 此bean要被web.xml引用,和web.xml中的filtername同名 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/index.jsp" /> <!-- 没有认证返回地址 -->
        <property name="unauthorizedUrl" value="/index2.jsp" /> <!-- 没有授权返回地址 -->
        <property name="filterChainDefinitions">
            <value>            <!-- **代表任意子目录 -->
            /login/** = anon
            /user/** = authc,roles[admin]
            /test/** = authc,perms[测试用的lkkk]
            </value>
        </property>
    </bean>

    <!-- 用户授权/认证信息Cache, 采用EhCache  缓存 -->
    <bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"/>
    </bean>

    <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!-- 生成代理,通过代理进行控制 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor">
    </bean>

    <!-- 安全管理器 -->
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

 

shiro缓存(ehcache支持ehcache-shiro.xml)

Alt text

<ehcache updateCheck="false" name="shiroCache">

    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            />
</ehcache>

 

 

java代码实现

 

自定义密码认证

public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {
    public boolean doCredentialsMatch(AuthenticationToken token,
            AuthenticationInfo info) {
        UsernamePasswordToken usertoken = (UsernamePasswordToken) token; 

        //注意token.getPassword()拿到的是一个char[],不能直接用toString(),它底层实现不是我们想的直接字符串,只能强转
        Object tokenCredentials = Encrypt.md5(String.valueOf(usertoken.getPassword()),usertoken.getUsername());  
        Object accountCredentials = getCredentials(info);  

        //将密码加密与系统加密后的密码校验,内容一致就返回true,不一致就返回false  
        return equals(tokenCredentials, accountCredentials);  
    }
}

 



util中定义shiro的MD5加密方法

public class Encrypt {
    /*
     * 散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,
     * 常见的散列算法如MD5、SHA等。一般进行散列时最好提供一个salt(盐),比如加密密码“admin”,
     * 产生的散列值是“21232f297a57a5a743894a0e4a801fc3”,
     * 可以到一些md5解密网站很容易的通过散列值得到密码“admin”,
     * 即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,
     * 如用户名和ID(即盐);这样散列的对象是“密码+用户名+ID”,这样生成的散列值相对来说更难破解。
     */

    //高强度加密算法,不可逆
    public static String md5(String password, String salt){
        return new Md5Hash(password,salt,2).toString();
    }

    public static void main(String[] args) {
        System.out.println(new Md5Hash("123456","tony",2).toString());
    }


}

 

 

自定义realm AuthRealm

在认证、授权内部实现机制中都有提到,最终处理都将交给Realm进行处理。 
因为在Shiro中,最终是通过Realm来获取应用程序中的用户、角色及权限信息的。 
通常情况下,在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说,Realm是专用于安全框架的DAO.

 
public class AuthRealm extends AuthorizingRealm{
    private UserService userService;
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    //授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("授权");
        //获取当前用户
        User user = (User)principals.fromRealm(getName()).iterator().next();
        //得到权限字符串
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        Set<Role> roles = user.getRoles();
        List<String> list = new ArrayList();
        for(Role role :roles){
            Set<Module> modules = role.getModules();
            for(Module m:modules){
                if(m.getCtype()==0){
                    //说明是主菜单
                    list.add(m.getCpermission());
                }
            }
        }

        info.addStringPermissions(list);
        return info;
    }
    //认证  登录
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        System.out.println("认证");
        UsernamePasswordToken upToken = (UsernamePasswordToken)token;

        User user = userService.findUserByName(upToken.getUsername());
        if(user==null){
            return null;
        }else{
            AuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
            return info;
        }

    }

}

 

 

修改传统登录为shiro登录

public class LoginAction extends BaseAction {

    private static final long serialVersionUID = 1L;

    private String username;
    private String password;

    private UserService userService;
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public String login() throws Exception {    
        /*
         * shiro登录方式:根据用户名获取密码,密码为null非法用户;有密码检查是否用户填写的密码
         * 登录成功后无需往httpsession中存放当前用户,这样就跟web容器绑定,关联太紧密;它自己创建
         * subject对象,实现自己的session。这个跟web容器脱离,实现松耦合。
         */

        //调用shiro判断当前用户是否是系统用户
        Subject subject = SecurityUtils.getSubject();   //得到当前用户
        //shiro是将用户录入的登录名和密码(未加密)封装到token对象中
        UsernamePasswordToken token = new UsernamePasswordToken(userName,password);

        try{
            subject.login(token);   //自动调用AuthRealm.doGetAuthenticationInfo

            //写seesion,保存当前user对象
            User curUser = (User)subject.getPrincipal();            //从shiro中获取当前用户
            System.out.println(curUser.getDept().getDeptName());    //让懒加载变成立即加载
            Set<Role> roles = curUser.getRoles();
            for(Role role :roles){
                Set<Module> moduless =  role.getModules();
                for(Module m :moduless)
                   System.out.println(m.getName());
            }
            session.put(SysConstant.CURRENT_USER_INFO, curUser);    //Principal 当前用户对象
        }catch(Exception ex){
            super.put("errorInfo","用户名密码错误,请重新填写!");
            ex.printStackTrace();

            return "login";
        }
        return SUCCESS;
    }

    public String logout(){
        session.remove(SysConstant.CURRENT_USER_INFO);      //删除session
        return "logout";
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

}

 

 

页面使用shiro标签

<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro"%>

        <shiro:hasPermission name="sysadmin">
        <span id="topmenu" onclick="toModule('sysadmin');">系统管理</span>
        </shiro:hasPermission>

 

 
posted @ 2016-10-24 14:16  woms  阅读(13162)  评论(1编辑  收藏  举报