美文网首页spring相关技术Java技术文章
使用shiro结合spring框架进行用户认证

使用shiro结合spring框架进行用户认证

作者: lunabird | 来源:发表于2016-02-07 17:02 被阅读3907次

Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。

HP-shiro-spring是一个简单的基于Spring实现shiro的例子,进行用户的身份认证,实现基于role的授权。该项目是由MyEclipse进行构建的动态web项目。

项目具体实现层次结构如下:


HP-shiro-spring-test.png
  1. 首先定义shiro.ini,用来指定用户身份和凭据。
[users]
root = secret, root
guest = guest, guest
gandhi = 12345, role1, role2
bose = 67890, role2

[roles]
root = *
role1 = filesystem:*,system:*
role2 = "calculator:add,subtract"

上面的shiro.ini文件定义了四个用户,格式为“用户名=密码,角色”;每个角色拥有一些权限。
root拥有所有的权限,role1拥有filesystem以及system的所有权限,role2拥有calculator的add和substract权限。这些权限在当前用户对系统资源进行访问的时候要用到。

2.定义配置文件
web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">
    <!-- 作用:在启动Web容器时,自动装配Spring applicationContext.xml的配置信息 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    
    
    <listener>
        <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
    </listener>    

    <!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml -->
    <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>

    <!-- Make sure any request you want accessible to Shiro is filtered. catches all 
        requests. Usually this filter mapping is defined first (before all others) to 
        ensure that Shiro works in subsequent filters in the filter chain: -->
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher> 
        <dispatcher>FORWARD</dispatcher> 
        <dispatcher>INCLUDE</dispatcher> 
        <dispatcher>ERROR</dispatcher>        
    </filter-mapping>
    
    
    <servlet>
        <servlet-name>springMvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    
    <!-- 
    <servlet>
        <servlet-name>login</servlet-name>
        <servlet-class>com.hp.shiro.simplerbac.controller.LoginServlet</servlet-class>
    </servlet>

    <servlet>
        <servlet-name>logout</servlet-name>
        <servlet-class>com.hp.shiro.simplerbac.controller.LogoutServlet</servlet-class>
    </servlet>
    
    <servlet>
        <servlet-name>home</servlet-name>
        <servlet-class>com.hp.shiro.simplerbac.controller.HomeServlet</servlet-class>
    </servlet>
     -->
    <servlet-mapping>
        <servlet-name>springMvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
<!-- 
    <servlet-mapping>
        <servlet-name>login</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>logout</servlet-name>
        <url-pattern>/logout</url-pattern>
    </servlet-mapping>
    
    <servlet-mapping>
        <servlet-name>home</servlet-name>
        <url-pattern>/home/*</url-pattern>
    </servlet-mapping>
 -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

springMvc-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
    <!-- is actually rather pointless. It declares explicit support 
    for annotation-driven MVC controllers (i.e.@RequestMapping, 
    @Controller, etc), even though support for those is the default
     behaviour  
     当我们需要controller返回一个map的json对象时,可以设定<mvc:annotation-driven />
     会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter
      两个bean,是spring MVC为@Controllers分发请求所必须的。并提供了:数据绑定支持,
    @NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,读写XML
    的支持(JAXB),读写JSON的支持(Jackson)-->
    <mvc:annotation-driven />
    
    <!-- 指定静态资源的位置,例如js,css和图片等文件,放到webroot文件夹下 -->
    <mvc:resources mapping="/css/**" location="/css/" />
    <mvc:default-servlet-handler />
    
    <!-- 启用spring mvc注解  例如 @Required, @Autowired, @PostConstruct-->
    <context:annotation-config />
    
    <!-- 设置使用注解的类所在的包名 -->
    <context:component-scan base-package="com.hp.shiro.simplerbac.controller" />
    
    <!--完成请求和注解pojo的映射。 
    当我们需要controller返回一个map的json对象时,可以设定<mvc:annotation-driven />,
    同时设定<mvc:message-converters> 标签,设定字符集和json处理类 -->
    <bean
        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean
                    class="org.springframework.http.converter.StringHttpMessageConverter">
                    <property name="supportedMediaTypes">
                        <list>
                            <value>text/plain;charset=UTF-8</value>
                        </list>
                    </property>
                </bean>
            </list>
        </property>
    </bean>
    
    
    <!-- <bean
        class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />
         -->
         
    <!-- 视图解析器,对转向页面的路径解析。prefix:前缀,suffix:后缀 -->
    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
    
    <!-- <bean id="multipartResolver"  
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/> -->

</beans>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
    <bean id="iniRealm" class="org.apache.shiro.realm.text.IniRealm">
        <property name="resourcePath" value="classpath:/shiro.ini" />
    </bean>

    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="iniRealm" />
    </bean>
    <!--Shiro 生命周期处理器--> 
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

    <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>
    
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="/login" />
        <property name="successUrl" value="/home/" />

        <property name="filterChainDefinitions">
            <value>
                /home/** = authc
            </value>
        </property>
    </bean>


</beans>

3.然后定义ProtectedService.java来实现功能。

package com.hp.shiro.simplerbac.bean;

import java.io.File;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.shiro.authz.annotation.RequiresPermissions;

public class ProtectedService {
    private static final List<String> USERS = Arrays.asList("root","guest","gandhi","bose");
    
    private static final List<String> ROLES = Arrays.asList("root","guest","role1","role2");
    
    @RequiresPermissions("user-roles:read")
    public List<String> getUsers() {
        return USERS;
    }
    
    @RequiresPermissions("user-roles:read")
    public List<String> getRoles() {
        return ROLES;
    }
    
    @RequiresPermissions("system:read:time")
    public Date getSystemTime() {
        return Calendar.getInstance().getTime();
    }
    
    @RequiresPermissions("calculator:add")
    public int sum(int a, int b) {
        return a+b;
    }
    
    @RequiresPermissions("calculator:subtract")
    public int diff(int a, int b) {
        return a-b;
    }
    
    @RequiresPermissions("filesystem:read:home")
    public List<String> getHomeFiles() {
        File homeDir = new File(System.getProperty("user.home"));
        return Arrays.asList(homeDir.list());
    }

    public String getGreetingMessage(String name) {
        return String.format("Hello %s",name);
    }
}

使用了@RequiresPermissions()注解来表示每一个方法的需要的permission,没有该注解的getGreetingMessage(String name)方法不要求任何权限。

4.定义两个Controller,分别是登陆/登出页面的controller和成功登陆以后完成访问系统资源功能的controller。

package com.hp.shiro.simplerbac.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;


@Controller
public class LoginController {
    @RequestMapping(value="login", method=RequestMethod.GET)
    public String login(HttpServletRequest req){
        if (SecurityUtils.getSubject().isAuthenticated()) {
            return "redirect:/home";
        } else {
            return "login";
        }
    }

    @RequestMapping(value="login", method=RequestMethod.POST)
    public String login(HttpServletRequest req,RedirectAttributes redirectAttributes,Model model) {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        System.out.println("access to login");
        System.out.println(username+","+password);
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        String errorMessage = null;
        try {
            SecurityUtils.getSubject().login(token);
        } catch (AuthenticationException e) {
            errorMessage = "user name doesn't exist or wrong password";
        }
        if(null == errorMessage) {
            redirectAttributes.addAttribute("username", username);
            return "redirect:/home";
        } else {
            System.out.println(errorMessage);
            req.setAttribute("errorMessage",errorMessage);
            return "login";
        }
    }
    
    @RequestMapping(value="logout")
    public String logout(HttpServletRequest req){
        SecurityUtils.getSubject().logout();
        return "redirect:/login";
    }
    
    
}

package com.hp.shiro.simplerbac.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.SecurityUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.hp.shiro.simplerbac.bean.ProtectedService;

@Controller
public class HomeController {
    @RequestMapping(value="home")
    public String home(HttpServletRequest req, HttpServletResponse response, Model model){
//      System.out.println("access to home controller.");
        String username = (String)SecurityUtils.getSubject().getPrincipal();
//      System.out.println("username:"+username);
        model.addAttribute("username", username);
        String method = req.getParameter("method");
//      System.out.println("method:"+method);
        /*
         * method可能的值value包括:
         *  <input type="hidden" name="method" value="getUsers"/>
         *  <input type="hidden" name="method" value="getRoles"/>
         *  <input type="hidden" name="method" value="getSystemTime"/>
         *  <input type="hidden" name="method" value="sum"/>
         *  <input type="hidden" name="method" value="diff"/>
         *  <input type="hidden" name="method" value="getHomeFiles"/>
         *  <input type="hidden" name="method" value="getGreetingMessage"/>
         */

        ProtectedService protectedService = new ProtectedService();
        
        try {
            if ("getUsers".equals(method)) {
                model.addAttribute("users", protectedService.getUsers());
            } else if ("getRoles".equals(method)) {
                model.addAttribute("roles", protectedService.getRoles());
            } else if ("getSystemTime".equals(method)) {
                model.addAttribute("systemTime", protectedService.getSystemTime());
            } else if ("sum".equals(method)) {
                int a = Integer.parseInt(req.getParameter("a"));
                int b = Integer.parseInt(req.getParameter("b"));
                model.addAttribute("sum",protectedService.sum(a, b));
            } else if ("diff".equals(method)) {
                int a = Integer.parseInt(req.getParameter("a"));
                int b = Integer.parseInt(req.getParameter("b"));
                model.addAttribute("diff",protectedService.diff(a, b));
            } else if ("getHomeFiles".equals(method)) {
                model.addAttribute("homeFiles",protectedService.getHomeFiles());
            } else if ("getGreetingMessage".equals(method)) {
                String name = req.getParameter("name");
                model.addAttribute("greetingMessage",protectedService.getGreetingMessage(name));
            }
        } catch(Exception e) {
            model.addAttribute("errorMessage", e.getMessage());
        }
        
        return "home";
    }
}

5.定义jsp文件和css样式文件,具体代码参见工程源码HP-shiro-spring

总结:
该项目的主要目的是对spring的shiro的配置文件进行一个梳理,了解它俩结合的具体配置方式。
该项目将用户名和密码简单的存放在文本文件中,而且是明文存储,以后需要迁移到数据库加密存储的形式。
参考开涛的博客进一步对shiro的功能进行探索。例如加密解密模块和session管理模块。

2016/06/22日更新:使用shiro+springmvc+mybatis实现的小例子,页面没有变化,添加了数据库的支持。
项目地址:https://github.com/lunabird/shiro-demo.git

相关文章

  • 使用shiro结合spring框架进行用户认证

    Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因...

  • Shiro 认证流程分析

    本文基于shiro 认证框架分析登录逻辑中是如何进行用户识别认证的,文章会涉及到shiro 的CacheManag...

  • Shiro快速入门

    一.Shiro简介 Shiro框架是和spring security框架作用差不多的一个安全认证授权框架,但它更加...

  • Apache Shiro的入门

    1,Apache Shiro是什么? Shiro是一种功能强大的,使用简单的Java安全框架,他执行认证(用户身份...

  • shiro初识,全面介绍架构精讲篇

    什么是shiro shiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证、用户授权。 spr...

  • Shiro 登陆认证 SimpleAuthenticationI

    1.shiro组件 Realm 使用shiro框架需要自己定义一个Realm来进行登陆信息以及权限信息的认证。可以...

  • spring security踩坑记

    spring security是spring boot里用来进行系统访问安全控制的框架,可以对登录的用户进行认证和...

  • 基于shiro的权限设计

    shiro介绍 Apache shiro是一个权限控制框架,它将安全认证抽取出来,实现用户身份认证,权限授权,加密...

  • Spring Boot 集成Shiro用户认证

    在Spring Boot中集成Shiro进行用户的认证过程主要可以归纳为以下三点: 定义一个ShiroConfig...

  • shiro小记

    shiro介绍 Shiro是一个Java平台的开源权限框架,用于认证和访问授权 shiro用户验证流程 流程1:主...

网友评论

本文标题:使用shiro结合spring框架进行用户认证

本文链接:https://www.haomeiwen.com/subject/mqsykttx.html