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>
目录
相关文章
|
2月前
|
JSON 安全 算法
|
2天前
|
存储 安全 Java
Spring Security 入门
Spring Security 是 Spring 框架中的安全模块,提供强大的认证和授权功能,支持防止常见攻击(如 CSRF 和会话固定攻击)。它通过过滤器链拦截请求,核心概念包括认证、授权和自定义过滤器。配置方面,涉及密码加密、用户信息服务、认证提供者及过滤器链设置。示例代码展示了如何配置登录、注销、CSRF防护等。常见问题包括循环重定向、静态资源被拦截和登录失败未返回错误信息,解决方法需确保路径正确和添加错误提示逻辑。
Spring Security 入门
|
26天前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
48 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
4月前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
19天前
|
Java 数据库 数据安全/隐私保护
轻松掌握Spring依赖注入:打造你的登录验证系统
本文以轻松活泼的风格,带领读者走进Spring框架中的依赖注入和登录验证的世界。通过详细的步骤和代码示例,我们从DAO层的创建到Service层的实现,再到Spring配置文件的编写,最后通过测试类验证功能,一步步构建了一个简单的登录验证系统。文章不仅提供了实用的技术指导,还以口语化和生动的语言,让学习变得不再枯燥。
36 2
|
23天前
|
Java 数据库连接 数据库
从入门到精通---深入剖析Spring DAO
在Java企业级开发中,Spring框架以其强大的功能和灵活性,成为众多开发者的首选。Spring DAO(Data Access Object)作为Spring框架中处理数据访问的重要模块,对JDBC进行了抽象封装,极大地简化了数据访问异常的处理,并能统一管理JDBC事务。本文将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring DAO,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
22 1
|
27天前
|
安全 Java 数据安全/隐私保护
如何使用Spring Boot进行表单登录身份验证:从基础到实践
如何使用Spring Boot进行表单登录身份验证:从基础到实践
45 5
|
27天前
|
监控 Java 数据安全/隐私保护
如何用Spring Boot实现拦截器:从入门到实践
如何用Spring Boot实现拦截器:从入门到实践
48 5
|
2月前
|
Java API Spring
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中拦截器的入门教程和实战项目场景实现的详细指南。
35 0
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
|
2月前
|
Java API Spring
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中过滤器的基础知识和实战项目应用的教程。
40 0
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现