Shiro安全框架与Spring的整合实战图文详解

本文介绍如何使用Apache Shiro框架进行权限认证与管理。包括Maven依赖配置、Shiro核心控制器设置、自定义Realm及密码比较器实现等关键技术点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.导入jar包(Maven工程):

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

2.Shiro核心控制器的配置:

<!-- Shiro核心控制器,一定放在struts2过滤器之前  filter-name这个名字的值将来还会在spring中用到  -->
   <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>

3.产生代理类的方式:

下面这行代码放置在applicationContext.xml的事务管理器声明之前。

<!-- 告诉spring生成shiro代理子类时,采用cglib方式生成 -->
<aop:aspectj-autoproxy proxy-target-class="true" />

4.Shiro的配置文件:

    <description>Shiro与Spring整合</description>
    <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 -->
    </bean>

    <!-- 自定义Realm域的编写 -->
    <bean id="authRealm" class="cn.itcast.shiro.AuthRealm">
        <!-- 注入自定义的密码比较器 -->
        <property name="credentialsMatcher" ref="customerCredentialsMatcher" ></property>
    </bean>

    <!-- 自定义的密码比较器 -->
    <bean id="customerCredentialsMatcher" class="cn.itcast.shiro.CustomerCredentialsMatcher"></bean>

     <!-- filter-name这个名字的值来自于web.xml中filter的名字 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!--登录页面  -->
        <property name="loginUrl" value="/index.jsp"></property>

        <!-- 登录成功后 -->      
        <!-- <property name="successUrl" value="/home.action"></property> -->

        <property name="filterChainDefinitions">
            <!-- /**代表下面的多级目录也过滤 -->
            <value>
                /index.jsp* = anon
                /home* = anon
                /sysadmin/login/login.jsp* = anon
                /sysadmin/login/loginAction_logout* = anon
                /login* = anon
                /logout* = anon
                /components/** = anon
                /css/** = anon
                /img/** = anon
                /js/** = anon
                /plugins/** = anon
                /images/** = anon
                /js/** = anon
                /make/** = anon
                /skin/** = anon
                /stat/** = anon
                /ufiles/** = anon
                /validator/** = anon
                /resource/** = anon
                /** = authc   
                /*.* = authc            </value>
        </property>
    </bean>

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

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

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

5.在applicationContext.xml中加载Shiro配置文件:

<!--组装其它 配置文件-->     
<import resource="classpath:spring/applicationContext-shiro.xml"/>

自定义AuthRealm类:

/**
 *Realm域的类需要继承AuthorizingRealm
 */
public class AuthRealm extends AuthorizingRealm {    
    @Autowired
    private UserService userService;    
    
    //授权
    /**
     * 这个方法在什么时候调用?当jsp页面上第一次出现shiro标签时就会自动调用
     */
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {        
        //1.从Shiro中取出用户对象
        User user = (User)pc.fromRealm(this.getName()).iterator().next();

        List<String> permission = new ArrayList<String>();        
        //2.通过对象关联查询,加载用户的角色列表
        Set<Role> roles = user.getRoles();        
        //3.遍历角色列表,得到每个角色
        for (Role role : roles) {
            Set<Module> modules = role.getModules();
            //对象导航查询,模块列表
            for (Module module : modules) {                
                if(!permission.contains(module)){
                    permission.add(module.getName());
                }

            }
        }        
        //生成一个返回值的对象
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermissions(permission);        
        return info;
    }    
    
    //认证   token参数代表用户在界面上输入的用户名和密码
    /**
     * 如果返回值为null,shiro就会抛出异常
     * 
     * 如果返回值不为null,程序就会自动调用密码比较器
     * 
     */
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {        //1.向下转型 
        final UsernamePasswordToken upToken = (UsernamePasswordToken) token;        
        //2.调用业务方法,实现根据用户名进行查询
        Specification<User> spec = new Specification<User>() {            
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {                
                return cb.equal(root.get("userName").as(String.class), upToken.getUsername());
            }
        };
        List<User> userList = userService.find(spec);        
        //3.判断
        if(userList!=null && userList.size()>0){
            User user = userList.get(0);            
            //三个参数,第一个参数Principal代表用户对象,第二个参数credentials 代表密码    第三个参数realmName代表realm域的名字
            return new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
        }        
        return null;
    }

}

自定义CustomerCredentialsMatcher密码比较器类:

/**
 * 密码比较器
 */
public class CustomerCredentialsMatcher extends SimpleCredentialsMatcher {    
    /**
     * 执行密码比较
     * 第一个参数:用户在界面上输入的用户名和密码
     * 第二个参数info:代表用户的全部信息
     * 
     * 返回值:
     *      true代表密码比较成功
     *      false代表密码比较失败,Shiro就会抛出异常
     */
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {        
        //1.向下转型 
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;        
        //2.调用Md5Hash算法  第一个参数:明文    第二个参数:盐   第三个参数:加盐的次数,代表轮换的次数
        //String uipwdEncrypt = new Md5Hash(new String(upToken.getPassword()), upToken.getUsername(), 2).toString();
        String uipwdEncrypt = Encrypt.md5(new String(upToken.getPassword()), upToken.getUsername());        
        //3.可以得到这个用户在数据库中的密文
        String dbpwd = info.getCredentials().toString();        
        return equals(uipwdEncrypt, dbpwd);
    }

}

LoginAction登陆Action类:

/**
 * @Description: 登录和退出类
 * 
 * 继承BaseAction的作用
 * 1.可以与struts2的API解藕合
 * 2.还可以在BaseAction中提供公有的通用方法
 */
@Namespace("/")
@Results({ 
    @Result(name="login",location="/WEB-INF/pages/sysadmin/login/login.jsp"),    
    @Result(name="success",location="/WEB-INF/pages/home/fmain.jsp"),    
    @Result(name="logout",location="/index.jsp")})
public class LoginAction extends BaseAction {    
    private static final long serialVersionUID = 1L;    
    private String username;    
    private String password;    
    @Action("loginAction_login")    
    public String login() throws Exception {        
        //SSH传统登录方式    
        //if(true){
        //    String msg = "登录错误,请重新填写用户名密码!";
        //    this.addActionError(msg);
        //    throw new Exception(msg);
        //}
        //User user = new User(username, password);
        //User login = userService.login(user);
        //if (login != null) {
        //    ActionContext.getContext().getValueStack().push(user);
        //    session.put(SysConstant.CURRENT_USER_INFO, login);    //记录session
        //    return SUCCESS;
        //}
        //return "login";

        if(UtilFuns.isEmpty(username)){            
            return "login";
        }        
        try {            
            //1.得到Subject
            Subject subject = SecurityUtils.getSubject();            
            //2.调用登录方法
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);

            subject.login(token);//调用登录

            //3.从Shiro中取出用户信息
            User user = (User)subject.getPrincipal();            
            //4.将用户信息放入session域中
            session.put(SysConstant.CURRENT_USER_INFO, user);


        } catch (Exception e) {
            e.printStackTrace();
            request.put("errorInfo", "对不起,用户名或密码错误,登录失败!");            
            return "login";
        }        
        return SUCCESS;
    }    
    //退出
    @Action("loginAction_logout")    public String logout(){
        session.remove(SysConstant.CURRENT_USER_INFO); //删除session
        SecurityUtils.getSubject().logout(); //登出
        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;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值