봄 프로젝트 및 기사 (5) ---- 시로 로그인 인증의 실현

여기에서 사용 된 무엇이 시로에서, 시로, 첫번째보기를 다음, 사용자 인증 및 방문 권한 검증을 수행하는

아파치 시로 인증, 권한 부여, 암호 및 세션 관리를위한 강력하고 사용하기 쉬운 자바의 보안 프레임 워크입니다. 시로의 쉬운이 API의 사용을 이해하는, 빠르고 쉽게 최대 규모의 모바일 네트워크 애플리케이션 및 엔터프라이즈 애플리케이션에 가장 작은에서 모든 응용 프로그램에 액세스 할 수 있습니다.

주요 기능
세 가지 핵심 구성 요소 : 제목, 보안 관리자 및 렐름.
  • 제목 : 즉, "현재 사용자." 그러나, 시로,이 개념의 주제는 사람뿐만 아니라이, 그것은 타사 과정, 배경 계정 (데몬 계정) 또는 이와 유사한 일이 될 수 있습니다. 그것은 단순히 "뭔가 현재 인터랙티브 소프트웨어"을 의미합니다.
  • 주제는 현재 사용자의 보안 작업을 나타냅니다, 보안 관리자는 보안 작업의 모든 사용자를 관리 할 수 ​​있습니다.
  • 보안 관리자 : SecurityManager에 의해 내부 컴포넌트 인스턴스를 관리하는 시로 일반적인 외관 패턴 시로의 코어 프레임 워크이며, 그것을 통해 서비스 보안 관리의 다양한 제공한다.
  • 영역 : 시로 및 애플리케이션 보안 데이터 사이의 "다리"또는 "커넥터"와 같은 영역의 역할을합니다. 즉, 경우에 사용자 인증을 수행하는 (로그인) 및 권한 부여 (액세스 제어) 검증, 시로 애플리케이션 구성 영역에있는 사용자 및 권한 정보를 찾는다.
  이러한 의미에서, 영역은 본질적으로 안전 관련 DAO이다 : 그것은 데이터 소스 연결 정보를 캡슐화하고, 필요한 경우는 시로에 관련 데이터를 제공 할 것입니다. 시로를 구성 할 때 인증 및 (또는) 인증을위한 적어도 하나 개의 영역을 지정해야합니다. 구성 여러 영역은 가능하지만, 적어도 하나가 필요합니다.
  시로 내장 영역은 LDAP로, (디렉토리 일명) 보안 데이터 소스의 큰 숫자를 연결할 수 있으며, INI 텍스트 구성 자원 및 속성 파일과 유사 관계형 데이터베이스 (JDBC). 기본 영역이 수요를 충족 할 수없는 경우, 당신은 또한 자신의 영역 구현 대신 사용자 정의 데이터 소스를 삽입 할 수 있습니다.
 
시로 코어는 다음과 같은 주요 카테고리를 가지고 :
  • 인증 인증 / 로그인 사용자가 해당 식별 정보를 갖고 있지 않은지 확인;
  • 인증 권한 부여, 확인 즉, 권한, 사용자가 권한을 인증되었는지 확인;
  • 에 사용자가 로그인 한 후 세션을 아니 출구가 없을 때까지이 세션의 모든 정보의 세션 관리자 세션 관리자;
  • 보호 데이터 보안 암호화 암호화
  • 웹 지원 웹 지원은 매우 쉽게 웹 환경에 통합 할 수 있습니다;
  • 사용자가 역할과, 자신의 사용자 정보, 로그인과 같은 캐시를 캐싱 / 권한이 효율성을 높일 수있는 모든 시간을 확인할 필요가 없습니다;
  • 동시성 시로 지원 멀티 스레드 애플리케이션 스레드에서 그와 같은 개방형 다른 스레드를 확인하기 위해이 권한은 자동으로 과거에 전파 될 수있다;
  • 테스트는 테스트 지원을 제공합니다;
  • (가 허용되는 경우) 정체성을 다른 사용자에 대한 액세스를 허용하는 척 사용자로 실행;
  • 나 나 기억해 기억이 첫 번째 로그인 후, 다시 로그인 한 다음에 오지 않는 아주 일반적인 기능, 즉이다.

 

 

 Shiroe 아키텍처는 아마 차트로 볼 수 시로 아키텍처 주로 :

  • 제목 : 본체는 사용자가 프로그램 액세스에 따라 시스템 일 수 있습니다, 시스템은 대상 인증, 권한 부여해야합니다.
  • 보안 관리자 : 인증 및 권한 부여에 대한 보안 관리 기관은 보안 관리자를 통해 이루어집니다.
  • 인증 : 인증에 의해 인증을위한 최종 인증 기관.
  • 권한 부여 : 권한있는 기관은 인증에 의해 궁극적으로 권한을 부여합니다.
  • SessionManager에가 : 웹 응용 프로그램 세션 관리 세션은 일반적으로 웹 컨테이너에 의해 관리되고, 시로는 관리 세션을 제공합니다.
  • sessionDao 작성자 : sessionDao 관리 세션 데이터,
  • cacheManager : 캐쉬 관리 주로 세션 및 권한 부여 데이터 캐시, 같은 cacheManager에 의한 인증 데이터 캐시 관리와 같은, 데이터 캐시 관리의으로 Ehcache 통합.
  • 범위 : 영역 액세스 인증을 통해 소스에 대응하는 필드 데이터, 인증 데이터.
  • 암호 : 암호 관리 구성 요소는 암호화 / 복호화 세트를 제공하고, 개발을 촉진. 이러한 일반적인 해시, 암호화 / 암호 해독 및 기타 기능을 제공하는 등.

다음과 같이 인증 순서도

 

 다음으로, 우리는이 프로젝트에 직접 구성

첫째, 받는다는의 shiro1.5가 자동으로 다운로드하지 다운로드, 당신은 당신의 자신의 로컬 저장소로 다운로드 할 수 있습니다 할 수있는 치어 항아리 패키지에 도입 또는 1.4.2에 수정

<!--第三方日志库-->
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
    </dependency>

    <!--Commons Collections增强了Java集合框架。 它提供了几个功能来简化收集处理。 它提供了许多新的接口,实现和实用程序-->
    <dependency>
      <groupId>commons-collections</groupId>
      <artifactId>commons-collections</artifactId>
      <version>3.2.2</version>
    </dependency>
    <!--shiro核心-->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>${org.apache.shiro.version}</version>
    </dependency>
    <!--shiro整合web-->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-web</artifactId>
      <version>${org.apache.shiro.version}</version>
    </dependency>
    <!--shiro应用缓存-->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-ehcache</artifactId>
      <version>${org.apache.shiro.version}</version>
    </dependency>
    <!--整合spring-shiro-->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>${org.apache.shiro.version}</version>
    </dependency>

首先需要在web.xml中配置过滤器,帮助我们拦截请求

  <!--配置shiro过滤器,拦截所有请求-->
  <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>
  <!--配置shiro的过滤路径-->
  <filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

接下来配置application-shiro.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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--配置realm数据源-->
    <bean id="employeeRealm" class="com.yang.web.realm.EmployeeRealm">
        <property name="credentialsMatcher" ref="credentialsMatcher" />
    </bean>
    <!--配置shiro过滤器-->
    <bean id="formFilter" class="com.yang.web.filter.FormFilter" />

    <!--配置shiro安全管理器-->
    <bean id="securityManage" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="employeeRealm" />
        <!--配置缓存-->
        <property name="cacheManager" ref="ehCache" />
    </bean>

    <!--使用第三方去扫描shiro的注解-->
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManage" />
    </bean>

    <!--配置shiro过滤器-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!--
            配置登陆认证的路径
            如果没有配置该路径,对于没有认证过的请求,会跳转到login.jsp
            如果配置了该路径:
            如果请求是loginUrl的路径,那就会去做认证
            其他请求,会去执行对应login的请求
        -->
        <property name="loginUrl" value="/login" />
        <!--重新配置表单监听的过滤器-->
        <property name="filters">
            <map>
                <entry key="authc" value-ref="formFilter" />
            </map>
        </property>
        <property name="securityManager" ref="securityManage"/>
        <!--配置shiro的过滤器pattern-->
        <property name="filterChainDefinitions">
            <value>
                /static/** = anon  <!--不需要进行登陆验证-->
                /login.jsp = anon  <!--不需要进行登陆验证-->
                /logout = logout   <!--配置注销接口-->
                /** = authc   <!--除了上述请求外,都需要进行登陆验证-->
            </value>
        </property>
    </bean>

    <!--
        配置代理
        true:使用cglib的方式
        false:使用jdk接口动态代理
    -->
    <aop:config proxy-target-class="true" />
    <!--凭证匹配器-->
    <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <!--散列算法-->
        <property name="hashAlgorithmName" value="md5" />
        <!--散列算法-->
        <property name="hashIterations" value="2" />
    </bean>
    <!--缓存管理器-->
    <bean id="ehCache" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml" />
    </bean>
</beans>

需要在springMvc的配置文件中导入这个包

<!--导入shiro的配置-->
    <import resource="classpath:application-shiro.xml" />

我们在配置shiro是,配置了缓存管理器,需要创建配置文件

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
    <defaultCache
            maxElementsInMemory="1000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

我们在shiro中配置了loginUrl,因此需要在控制层实现对应的url

package com.yang.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;


/**
 * 配置了loginUrl,需要设置对应的函数
 */
@Controller
public class LoginController {
    @RequestMapping("/login")
    public String login() {
        return "redirect:login.jsp";
    }
}

接下来需要配置验证以及授权的realm

package com.yang.web.realm;

import com.yang.domain.Employee;
import com.yang.service.EmployeeService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;

/**
 * 设置员工权限realm,需要继承authorizingRealm
 */
public class EmployeeRealm extends AuthorizingRealm {

    /*注入*/
    @Autowired
    private EmployeeService employeeService;

    /*认证*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("认证,来啦");

        // 获取身份信息
        String username = (String) token.getPrincipal();
        System.out.println(username);
        // 根据当前用户名查看当前是否存在当前用户
        Employee employee = employeeService.getEmployeeByName(username);
        // 如果没有查询到employee、,就返回为空
        if (employee == null) {
            return null;
        }
        // 进行认证
        // 认证的参数,主体,正确的密码,盐,还有当前realm的名称
        return new SimpleAuthenticationInfo(employee, employee.getPassword(), ByteSource.Util.bytes("!@#QAZwsx"), this.getName());
    }

    /*授权*/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 获取客户主体
        Employee employee = (Employee) principalCollection.getPrimaryPrincipal();
        // 声明角色集合
        List<String> roles = null;
        // 声明权限集合
        List<String> permissions = null;
        // 判断该角色是否是管理员
        if (employee.getAdmin()) {
            // 是管理员,增加所有权限
            permissions = new ArrayList<>();
            roles = new ArrayList<>();
            // 获取所有权限
            permissions.add("*:*");
        } else {
            // 查询该员工所拥有的角色集合
            roles = employeeService.getRoleByEmployeeId(employee.getId());
            // 查询该员工所有的权限集合
            permissions = employeeService.getPermissionByEmployeeId(employee.getId());
        }
        // 添加授权信息
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(roles);
        info.addStringPermissions(permissions);
        return info;
    }
}

为了获取验证结果,我们需要配置form表单过滤器

package com.yang.web.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yang.domain.AjaxRes;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

/**
 * 这个等登陆成功会调用,为了通知web,在这里面重写方法,重写表单监听的过滤器成功以及失败的方法
 */
public class FormFilter extends FormAuthenticationFilter {
    /*当认证成功的时候,会进行调用*/
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        System.out.println(subject);
        // 因为回传信息包含中文,先设置字节码
        response.setCharacterEncoding("utf-8");
        AjaxRes ajaxRes = new AjaxRes();
        ajaxRes.setSuccess(true);
        ajaxRes.setMsg("登陆成功!");
        // 把对象转化为json字符串
        String resString = new ObjectMapper().writeValueAsString(ajaxRes);

        // 将字符串写入响应对象
        response.getWriter().print(resString);
        // return false 才会往下走,否则就组织了
        return false;
    }

    /*当认证失败的时候,就会调用,e就是抛出异常*/
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {

        // 设置响应数据
        AjaxRes ajaxRes = new AjaxRes();
        ajaxRes.setSuccess(false);
        if (e != null) {
            // 获取异常类的名称
            String name = e.getClass().getName();
            // 没有账号
            if (name.equals(UnknownAccountException.class.getName())) {
                ajaxRes.setMsg("账号不正确");
            } else if (name.equals(IncorrectCredentialsException.class.getName())) {
                ajaxRes.setMsg("密码不正确");  // 密码有误异常
            } else {
                ajaxRes.setMsg("不确认错误");  // 其他错误
            }
        }
        // 序列化返回结果并且放置到响应对象中
        try {
            String resString = new ObjectMapper().writeValueAsString(ajaxRes);
            response.setCharacterEncoding("utf-8");
            response.getWriter().print(resString);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        System.out.println(ajaxRes.getMsg());
        // return false 才会往下走,否则就阻止了
        // return true;  // 默认是返回true
        return false;
    }
}

同时对于授权的使用,前端

<shiro:hasPermission name="employee:add">
        <a href="#" class="easyui-linkbutton" data-options="iconCls:'icon-add',plain:true" id="add">添加</a>
    </shiro:hasPermission>

后端

    /*编辑员工*/
    @RequestMapping("/employee/update")
    @ResponseBody
    @RequiresPermissions("employee:update")  // 配置权限
    public AjaxRes employeeUpdate(Employee employee){
        return employeeService.updateEmployee(employee);
    }

为了处理异常,比如没有权限。一般在该类下写一个方法捕捉异常并处理

    /*
        设置异常处理
        参数method就是发生异常的方法
    */
    @ExceptionHandler(AuthorizationException.class)
    public void handleShiroException(HandlerMethod method, HttpServletResponse response)throws Exception{
        /*如果授权异常,则跳转授权页面*/
        // 获取异常方法中的是否是json请求
        ResponseBody methodAnnotation = method.getMethodAnnotation(ResponseBody.class);
        if(methodAnnotation != null){
            // 这个就是ajax的请求
            AjaxRes ajaxRes = new AjaxRes();
            ajaxRes.setSuccess(false);
            ajaxRes.setMsg("没有权限操作");
            String res = new ObjectMapper().writeValueAsString(ajaxRes);
            response.setCharacterEncoding("utf-8");
            response.getWriter().print(res);
        }else{
            response.sendRedirect("error-permission.jsp");
        }
    }

 

  

추천

출처www.cnblogs.com/yangshixiong/p/12307098.html