前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Shiro整合Spring实现登录验证和授权之入门

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

作者头像
CBeann
发布2023-12-25 16:34:09
2410
发布2023-12-25 16:34:09
举报
文章被收录于专栏:CBeann的博客CBeann的博客

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

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

创建项目

项目结构如下图所示

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

代码语言:javascript
复制
<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>

代码语言:javascript
复制
<?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的配置文件

代码语言:javascript
复制
<?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(不需要修改)

代码语言:javascript
复制
<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

代码语言:javascript
复制
<?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>

实体类

代码语言:javascript
复制
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方法

代码语言:javascript
复制
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权限

代码语言:javascript
复制
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层

代码语言:javascript
复制
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盐值加密算法

代码语言:javascript
复制
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

代码语言:javascript
复制
<%@ 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

代码语言:javascript
复制
<%@ 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

代码语言:javascript
复制
<%@ 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

代码语言:javascript
复制
<%@ 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

代码语言:javascript
复制
<%@ 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

代码语言:javascript
复制
<%@ 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>
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-03-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Shiro简介
  • 项目目的
    • 验证:
      • 授权:
        • 数据:
        • 创建项目
          • 项目结构如下图所示
            • 修改pom.xml文件,添加依赖
              • Spring配置文件
                • SpringMVC的配置文件
                  • ehcache.xml(不需要修改)
                    • 修改web.xml文件,添加Spring的listener,SpringMVC的servlet,Shiro的filter
                      • 实体类
                        • 控制层
                          • Realm,用于验证和授权的类(核心)
                            • service层
                              • MD5盐值加密算法
                                • admin.jsp
                                  • index.jsp
                                    • list.jsp
                                      • login.jsp
                                        • Unauthorized.jsp
                                          • User.jsp
                                          相关产品与服务
                                          容器服务
                                          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                                          领券
                                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档