Shiro整合Spring实现登录验证和授权之入门

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: Shiro整合Spring实现登录验证和授权之入门

Shiro简介


 Shiro官网


Apache Shiro是一个功能强大、灵活的,开源的安全框架。它可以干净利落地处理身份验证、授权、企业会话管理和加密。


Apache Shiro的首要目标是易于使用和理解。安全通常很复杂,甚至让人感到很痛苦,但是Shiro却不是这样子的。一个好的安全框架应该屏蔽复杂性,向外暴露简单、直观的API,来简化开发人员实现应用程序安全所花费的时间和精力。


Shiro能做什么呢?


●验证用户身份

●用户访问权限控制,比如:1、判断用户是否分配了一定的安全角色。2、判断用户是否被授予完成某个操作的权限

●在非 web 或 EJB 容器的环境下可以任意使用Session API

●可以响应认证、访问控制,或者 Session 生命周期中发生的事件

●可将一个或以上用户安全数据源数据组合成一个复合的用户 “view”(视图)

●支持单点登录(SSO)功能

●支持提供“Remember Me”服务,获取用户关联信息而无需登录


等等——都集成到一个有凝聚力的易于使用的API。


Shiro 致力在所有应用环境下实现上述功能,小到命令行应用程序,大到企业应用中,而且不需要借助第三方框架、容器、应用服务器等。当然 Shiro 的目的是尽量的融入到这样的应用环境中去,但也可以在它们之外的任何环境下开箱即用。


Authentication(认证), Authorization(授权), Session Management(会话管理), Cryptography(加密)被 Shiro 框架的开发团队称之为应用安全的四大基石。那么就让我们来看看它们吧:


Authentication(认证):用户身份识别,通常被称为用户“登录”

Authorization(授权):访问控制。比如某个用户是否具有某个操作的使用权限。

Session Management(会话管理):特定于用户的会话管理,甚至在非web 或 EJB 应用程序。

Cryptography(加密):在对数据源使用加密算法加密的同时,保证易于使用。


还有其他的功能来支持和加强这些不同应用环境下安全领域的关注点。特别是对以下的功能支持:


●Web支持:Shiro 提供的 web 支持 api ,可以很轻松的保护 web 应用程序的安全。

●缓存:缓存是 Apache Shiro 保证安全操作快速、高效的重要手段。

●并发:Apache Shiro 支持多线程应用程序的并发特性。

●测试:支持单元测试和集成测试,确保代码和预想的一样安全。

●“Run As”:这个功能允许用户假设另一个用户的身份(在许可的前提下)。

●“Remember Me”:跨 session 记录用户的身份,只有在强制需要时才需要登录。


项目目的


  验证:


                在表单输入用户名和(明文)密码,和数据库中的(密文)密码比对,设置好Shiro的加密算法后自动帮我们比对,如果和用户名和密码不正确,重定向到当前页面,并且显示错误信息,登录成功则则跳转到list.jsp


 授权:


               登录成功后,list.jsp里面有user.jsp(有user权限的才能访问),admin.jsp(有admin权限才能访问),如果没有权限,自动跳转到设置好的/unauthorized.jsp没有权限的页面,从而体现授权的功能


 数据:


             数据是静态数据,在UserService里面静态生成的,有user(密码:123456)和admin(密码:123456)两个用户,下图中的字符串是经过MD5加密算法后存到数据库中的数据,比对是从页面获得的(明文)数据经过加密算法和数据的比对


静态数据:admin   123456


               user        123456


1.png


其中,user用户有user权限,admin用户有admin和user权限


项目下载


如果不是Maven构建项目,我整合的jar比较乱


创建项目


项目结构如下图所示


2.png


3.jpg


修改pom.xml文件,添加依赖


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.imooc</groupId>
  <artifactId>Spring-Shiro</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  <dependencies>
    <!-- shiro核心包 -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>1.2.5</version>
    </dependency>
    <!-- 添加shiro web支持 -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-web</artifactId>
      <version>1.2.5</version>
    </dependency>
    <!-- 添加shiro spring支持 -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>1.2.5</version>
    </dependency>
    <!-- 添加Shiro 缓存 -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-ehcache</artifactId>
      <version>1.2.2</version>
    </dependency>
    <!-- 数据库 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.39</version>
    </dependency>
    <!-- 数据库连接池 c3p0 -->
    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
    </dependency>
    <!-- Spring -->
    <!-- 添加spring支持 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>4.3.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>4.3.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.3.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>4.3.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>4.3.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>4.3.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>4.3.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>4.3.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>4.3.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>4.3.10.RELEASE</version>
    </dependency>
    <!-- Servlet -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
      <scope>provided</scope>
    </dependency>
    <!-- JSP -->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.1</version>
      <scope>provided</scope>
    </dependency>
    <!-- 添加jstl支持 -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <!-- 添加log4j日志 -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.21</version>
    </dependency>
  </dependencies>
</project>


Spring配置文件


重点是shiroFilter这个bean


还要注意:      


/jquery/** = anon    (此项目jquery下放的是jquery.js文件,当页面访问是也是一个url,设置为可以匿名访问,否则js文件引入不进来)


<script type="text/javascript" src="jquery/jquery-3.2.1.min.js"></script>


<?xml version="1.0" encoding="UTF-8"?>
<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.xsd">
    <!-- 安全管理器 -->
    <!-- 配置 SecurityManager -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager" />
        <property name="realm" ref="jdbcRealm"></property>
        <property name="realms">
            <list>
                <ref bean="jdbcRealm" />
                <!-- <ref bean="secondRealm"/> -->
            </list>
        </property>
    </bean>
    <!-- 缓存管理器 使用Ehcache实现 -->
    <!-- 配置 CacheManager. (需要加入 ehcache 的 依赖 包及配置文件) -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml" />
    </bean>
    <!-- 配置 Realm,实现加密和授权 -->
    <bean id="jdbcRealm" class="com.imooc.realms.ShiroRealm">
        <!-- 配置加密算法 -->
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!-- 加密的种类 -->
                <property name="hashAlgorithmName" value="MD5"></property>
                <!-- 加密的次数 -->
                <property name="hashIterations" value="1024"></property>
            </bean>
        </property>
    </bean>
    <!-- 配置 LifecycleBeanPostProcessor. 可以自定的来调用配置在 Spring IOC 容器中 shiro bean 
        的生命周期方法. -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
    <!-- 启用 IOC 容器中使用 shiro 的注解. 但必须在配置了 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>
    <!-- 配置 ShiroFilter. id 必须和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 
        一致. 若不一致, 则会抛出: NoSuchBeanDefinitionException. 因为 Shiro 会来 IOC 容器中查找和 <filter-name> 
        名字对应的 filter bean. -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <!-- 没有登录的用户请求需要登录的页面时自动跳转到登录页面,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的”/login.jsp”页面 -->
        <property name="loginUrl" value="/login.jsp" />
        <!-- 登录成功默认跳转页面,不配置则跳转至”/”。如果登陆前点击的一个需要登录的页面,则在登录自动跳转到那个需要登录的页面。不跳转到此 -->
        <property name="successUrl" value="/list.jsp" />
        <!-- 没有权限默认跳转的页面 -->
        <property name="unauthorizedUrl" value="/unauthorized.jsp" />
        <!-- 配置哪些页面需要受保护. 以及访问这些页面需要的权限. 1). anon 可以被匿名访问 2). authc 必须认证(即登录)后才可能访问的页面. 
            3). logout 登出. 4). roles 角色过滤器 -->
        <property name="filterChainDefinitions">
            <value>
                <!-- action可以匿名访问 -->
                /*.action=anon
                <!-- 登录界面,可以匿名访问 -->
                /login.jsp = anon
                         <!-- js文件可以匿名访问,重点,容易漏掉 -->                   
                           /jquery/** = anon
                <!-- 退出登录方法可以匿名访问 -->
                /logout = logout
                <!-- 授权 -->
                <!-- user.jsp只有user权限才能访问,否则跳转到/unauthorized.jsp -->
                /user.jsp=roles[user]
                <!-- admin.jsp只有admin权限才能访问,否则跳转到/unauthorized.jsp -->
                /admin.jsp=roles[admin]
                <!-- 剩下的所有都要认证后访问 -->
                /** = authc
            </value>
        </property>
    </bean>
</beans>


SpringMVC的配置文件


<?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:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:task="http://www.springframework.org/schema/task"
    xsi:schemaLocation="http://www.springframework.org/schema/beans    
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd    
    http://www.springframework.org/schema/tx    
    http://www.springframework.org/schema/tx/spring-tx-4.0.xsd   
    http://www.springframework.org/schema/context   
    http://www.springframework.org/schema/context/spring-context-4.0.xsd 
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.0.xsd  
    http://www.springframework.org/schema/mvc   
    http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
    http://www.springframework.org/schema/task
    http://www.springframework.org/schema/task/spring-task-4.0.xsd">
  <context:component-scan base-package="com.imooc.handlers"></context:component-scan>
  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/"></property>
    <property name="suffix" value=".jsp"></property>
  </bean>
  <mvc:annotation-driven></mvc:annotation-driven>
  <mvc:default-servlet-handler/>
</beans>


ehcache.xml(不需要修改)


<ehcache>
    <!-- Sets the path to the directory where cache .data files are created.
         If the path is a Java System Property it is replaced by
         its value in the running VM.
         The following properties are translated:
         user.home - User's home directory
         user.dir - User's current working directory
         java.io.tmpdir - Default temp file path -->
    <diskStore path="java.io.tmpdir"/>
   <!--  <cache name="authorizationCache"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>
    <cache name="authenticationCache"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>
    <cache name="shiro-activeSessionCache"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache> -->
    <!--Default Cache configuration. These will applied to caches programmatically created through
        the CacheManager.
        The following attributes are required for defaultCache:
        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.
        -->
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />
    <!--Predefined caches.  Add your cache configuration settings here.
        If you do not have a configuration for your cache a WARNING will be issued when the
        CacheManager starts
        The following attributes are required for defaultCache:
        name              - Sets the name of the cache. This is used to identify the cache. It must be unique.
        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.
        -->
    <!-- Sample cache named sampleCache1
        This cache contains a maximum in memory of 10000 elements, and will expire
        an element if it is idle for more than 5 minutes and lives for more than
        10 minutes.
        If there are more than 10000 elements it will overflow to the
        disk cache, which in this configuration will go to wherever java.io.tmp is
        defined on your system. On a standard Linux system this will be /tmp"
        -->
    <cache name="sampleCache1"
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="true"
        />
    <!-- Sample cache named sampleCache2
        This cache contains 1000 elements. Elements will always be held in memory.
        They are not expired. -->
    <cache name="sampleCache2"
        maxElementsInMemory="1000"
        eternal="true"
        timeToIdleSeconds="0"
        timeToLiveSeconds="0"
        overflowToDisk="false"
        /> -->
    <!-- Place configuration for your caches following -->
</ehcache>


修改web.xml文件,添加Spring的listener,SpringMVC的servlet,Shiro的filter


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  id="WebApp_ID" version="2.5">
  <!-- Spring -->
  <!-- needed for ContextLoaderListener -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <!-- Bootstraps the root web application context before servlet initialization -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!-- SpringMVC -->
  <servlet>
    <servlet-name>springDispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  <!-- 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>
  <filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>


实体类


package com.imooc.entity;
/*
 * User类,用于登录的类
 */
public class User {
  private Integer id;//主键
  private String username;//用户名
  private String password;//密码
  @Override
  public String toString() {
    return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
  }
  /**
   * @return the id
   */
  public Integer getId() {
    return id;
  }
  /**
   * @param id
   *            the id to set
   */
  public void setId(Integer id) {
    this.id = id;
  }
  /**
   * @return the username
   */
  public String getUsername() {
    return username;
  }
  /**
   * @param username
   *            the username to set
   */
  public void setUsername(String username) {
    this.username = username;
  }
  /**
   * @return the password
   */
  public String getPassword() {
    return password;
  }
  /**
   * @param password
   *            the password to set
   */
  public void setPassword(String password) {
    this.password = password;
  }
  public User(String username, String password) {
    this.username = username;
    this.password = password;
  }
  public User() {
  }
}


控制层


获得到表单的数据并且封装到UsernamePasswordToken对象,调用subject的login方法


package com.imooc.handlers;
import java.util.Map;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.ExpiredCredentialsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginHandler {
    /*
     * 登录action
     */
    @RequestMapping("/login.action")
    public String login(String username, String password, Map<String, Object> map) {
        System.out.println("LoginHandler   login...");
        // 获得当前Subject
        Subject currentUser = SecurityUtils.getSubject();
        // 验证用户是否验证,即是否登录
        if (!currentUser.isAuthenticated()) {
            String msg = "";
            // 把用户名和密码封装为 UsernamePasswordToken 对象
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            // remembermMe记住密码
            token.setRememberMe(true);
            try {
                // 执行登录.
                currentUser.login(token);
                // 登录成功...
                return "redirect:/list.jsp";
            } catch (IncorrectCredentialsException e) {
                msg = "登录密码错误";
                System.out.println("登录密码错误!!!" + e);
            } catch (ExcessiveAttemptsException e) {
                msg = "登录失败次数过多";
                System.out.println("登录失败次数过多!!!" + e);
            } catch (LockedAccountException e) {
                msg = "帐号已被锁定";
                System.out.println("帐号已被锁定!!!" + e);
            } catch (DisabledAccountException e) {
                msg = "帐号已被禁用";
                System.out.println("帐号已被禁用!!!" + e);
            } catch (ExpiredCredentialsException e) {
                msg = "帐号已过期";
                System.out.println("帐号已过期!!!" + e);
            } catch (UnknownAccountException e) {
                msg = "帐号不存在";
                System.out.println("帐号不存在!!!" + e);
            } catch (UnauthorizedException e) {
                msg = "您没有得到相应的授权!";
                System.out.println("您没有得到相应的授权!" + e);
            } catch (Exception e) {
                System.out.println("出错!!!" + e);
            }
            map.put("msg", msg);
            return "/login";
        }
        // 登录成功,重定向到list.jsp
        return "redirect:/list.jsp";
    }
}


Realm,用于验证和授权的类(核心)


doGetAuthenticationInfo用于实现验证,在application.xml中配置了此Realm的加密算法


doGetAuthorizationInfo用于实现授权,此项目中是用户默认用于user权限,如果用户名等于admin,还用于admin权限


package com.imooc.realms;
import java.util.HashSet;
import java.util.Set;
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.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
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 com.imooc.service.UserService;
/*
 * 继承AuthorizingRealm,实现doGetAuthenticationInfo(验证)方法和doGetAuthorizationInfo(授权方法)
 */
public class ShiroRealm  extends  AuthorizingRealm{
  /**
   * 方面用于加密
   * 参数:AuthenticationToken是从表单穿过来封装好的对象
   */
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("doGetAuthenticationInfo:"+token);
        //将AuthenticationToken强转为AuthenticationToken对象
        UsernamePasswordToken upToken=(UsernamePasswordToken)token;
        //获得从表单传过来的用户名
        String username=upToken.getUsername();
        //从数据库查看是否存在用户
        UserService userService=new UserService();
        //如果用户不存在,抛此异常
        if(!userService.selectUsername(username)){
          throw new UnknownAccountException("无此用户名!");
        }
        //认证的实体信息,可以是username,也可以是用户的实体类对象,这里用的用户名
        Object principal=username;    
        //从数据库中查询的密码   
        Object credentials=userService.selectPassword(username);           
        //颜值加密的颜,可以用用户名
        ByteSource credentialsSalt=ByteSource.Util.bytes(username);       
        //当前realm对象的名称,调用分类的getName()
        String realmName=this.getName();
        //创建SimpleAuthenticationInfo对象,并且把username和password等信息封装到里面
        //用户密码的比对是Shiro帮我们完成的
        SimpleAuthenticationInfo info=null;
        info=new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
        return info;
  }
  //用于授权
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    //从PrincipalCollection中获得用户信息
    Object principal=principals.getPrimaryPrincipal();
    System.out.println("ShiroRealm  AuthorizationInfo:"+principal.toString());
    //根据用户名来查询数据库赋予用户权限(查数据库)
    Set roles=new HashSet<>();
    roles.add("user");
    if("admin".equals(principal)){
      roles.add("admin");
    }
    SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(roles);
    return info;
    //return null;
  }
}


service层


package com.imooc.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import com.imooc.entity.User;
@Service
public class UserService {
  //用户的集合
  private List<User> users=new ArrayList<>();
  public UserService(){
    //从数据库查出来的用户名,密码,这是是静态数据
    users.add(new User("admin", "038bdaf98f2037b31f1e75b5b4c9b26e"));
    users.add(new User("user", "098d2c478e9c11555ce2823231e02ec1"));
  }
  //判断是否用户名是否存在
  public boolean selectUsername(String username){
    for (User user : users) {
      if(user.getUsername().equals(username)){
        return true;
      }
    }
    return false;
  }
  //根据用户返回查询的密码
    public String selectPassword(String username){
      for (User user : users) {
        if(user.getUsername().equals(username)){
          return user.getPassword();
        }
      }
      return "";
    }
}


MD5盐值加密算法


package com.imooc.test;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
public class MD5Test {
  public static void main(String[] args) {
    //加密额类型
    String algorithmName="MD5";
    //密码
    Object source="123456";
    //盐值加密的盐(一般用用户名做盐值)
    String username="user";
    ByteSource credentialsSalt=ByteSource.Util.bytes(username);
    //加密的次数
    int hashIterations=1024;
    //执行加密算法,返回加密结果
    Object o=new SimpleHash(algorithmName, source, credentialsSalt, hashIterations);
    System.out.println("MD5加密后的密码:"+o);
  }
}


admin.jsp


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
  <h4>Admin Page</h4>
</body>
</html>


index.jsp


<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    <title>My JSP 'index.jsp' starting page</title>
  <meta http-equiv="pragma" content="no-cache">
  <meta http-equiv="cache-control" content="no-cache">
  <meta http-equiv="expires" content="0">    
  <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
  <meta http-equiv="description" content="This is my page">
  <!--
  <link rel="stylesheet" type="text/css" href="styles.css">
  -->
  </head>
  <body>
    This is my JSP page. <br>
  </body>
</html>


list.jsp


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>    
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
  <h4>List Page</h4>
    <br/>
  <a href="user.jsp">user</a>
  <br/>
  <a href="admin.jsp">admin</a>
  <br/>
  <a href="logout">Logout</a>
</body>
</html>


login.jsp


<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="jquery/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
$(function(){
     var msg = '<%=request.getAttribute("msg")%>';//获得LoginMethod中返回的的信息
        if(msg!=null&&msg!="null"&&msg!=""){
            alert(msg);
        }
    });
</script>
<title>Insert title here</title>
</head>
<body>
    <h4>Login Page</h4>
    ${msg}
    <br>
    <br>
    <form action="login.action" method="POST">
        username: <input type="text" name="username" /> <br> <br>
        password: <input type="password" name="password" /> <br> <br>
        <input type="submit" value="Submit" />
    </form>
</body>
</html>


Unauthorized.jsp


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
  <h4>Unauthorized Page</h4>
</body>
</html>


User.jsp


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
  <h4>User Page</h4>
</body>
</html>
目录
相关文章
|
8天前
|
JSON 安全 算法
|
3月前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
3月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
3月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
18天前
|
Java API Spring
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中拦截器的入门教程和实战项目场景实现的详细指南。
15 0
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
|
18天前
|
Java API Spring
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中过滤器的基础知识和实战项目应用的教程。
17 0
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
|
3月前
|
Java 数据库连接 Spring
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
文章是关于Spring、SpringMVC、Mybatis三个后端框架的超详细入门教程,包括基础知识讲解、代码案例及SSM框架整合的实战应用,旨在帮助读者全面理解并掌握这些框架的使用。
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
|
3月前
|
NoSQL Java Redis
Redis6入门到实战------ 八、Redis与Spring Boot整合
这篇文章详细介绍了如何在Spring Boot项目中整合Redis,包括在`pom.xml`中添加依赖、配置`application.properties`文件、创建配置类以及编写测试类来验证Redis的连接和基本操作。
Redis6入门到实战------ 八、Redis与Spring Boot整合
|
3月前
|
SQL 数据库
Spring5入门到实战------13、使用JdbcTemplate操作数据库(批量增删改)。具体代码+讲解 【下篇】
这篇文章是Spring5框架的实战教程,深入讲解了如何使用JdbcTemplate进行数据库的批量操作,包括批量添加、批量修改和批量删除的具体代码实现和测试过程,并通过完整的项目案例展示了如何在实际开发中应用这些技术。
Spring5入门到实战------13、使用JdbcTemplate操作数据库(批量增删改)。具体代码+讲解 【下篇】
|
3月前
|
XML Java 数据格式
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
这篇文章是Spring5框架的AOP切面编程教程,通过XML配置方式,详细讲解了如何创建被增强类和增强类,如何在Spring配置文件中定义切入点和切面,以及如何将增强逻辑应用到具体方法上。文章通过具体的代码示例和测试结果,展示了使用XML配置实现AOP的过程,并强调了虽然注解开发更为便捷,但掌握XML配置也是非常重要的。
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解