Shiro集成Spring和SpringBoot

Shiro集成Spring和SpringBoot

1.Spring+JSP集成shiro

1.pom坐标
<?xml version="1.0" encoding="UTF-8"?>
<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>org.example</groupId>
    <artifactId>Shiro</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>4.1.1.RELEASE</spring.version>
        <!-- <shiro.version>1.2.2</shiro.version> -->
        <shiro.version>1.3.2</shiro.version>
    </properties>

    <dependencies>
        <!-- spring mvc -->

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>



        <!-- servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>


        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
            <scope>compile</scope>
        </dependency>



        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.2.3</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.2.3</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.2.3</version>
        </dependency>



        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>

        <!-- spring begin -->

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>


        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>



        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.10</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.10</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>


        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.0</version>
        </dependency>

        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.3</version>
        </dependency>

        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.8</version>
        </dependency>

        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.8.3</version>
        </dependency>

        <dependency>
            <groupId>commons-chain</groupId>
            <artifactId>commons-chain</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-math3</artifactId>
            <version>3.5</version>
        </dependency>



        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-digester3</artifactId>
            <version>3.2</version>
        </dependency>

        <dependency>
            <groupId>commons-net</groupId>
            <artifactId>commons-net</artifactId>
            <version>3.3</version>
        </dependency>

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib-nodep</artifactId>
            <version>3.2.5</version>
        </dependency>

        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.5</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-email</artifactId>
            <version>1.3.3</version>
        </dependency>
        <!-- dbcp -->
        <!-- <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId>
            <version>1.4</version> </dependency> <dependency> <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId> <version>2.2</version> </dependency> -->

        <!-- c3p0 -->

        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5-pre8</version>
        </dependency>



        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>mchange-commons-java</artifactId>
            <version>0.2.12</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.5</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.20</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.2.8</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.2</version>
        </dependency>

        <!-- shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>${shiro.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro.version}</version>
        </dependency>


        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>${shiro.version}</version>
        </dependency>

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.2</version>
        </dependency>


    </dependencies>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.mybatis.generator</groupId>
                    <artifactId>mybatis-generator-maven-plugin</artifactId>
                    <version>1.3.2</version>
                    <dependencies>
                        <dependency>
                            <groupId>mysql</groupId>
                            <artifactId>mysql-connector-java</artifactId>
                            <version>5.1.46</version>
                        </dependency>
                    </dependencies>
                    <configuration>
                        <!--配置文件的路径 -->
                        <configurationFile>${basedir}/src/main/resources/generatorConfig.xml</configurationFile>
                        <overwrite>true</overwrite>
                    </configuration>
                </plugin>

           <!--     <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>2.5.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <compilerArgument>-Xlint:all</compilerArgument>
                        <showWarnings>true</showWarnings>
                        <showDeprecation>true</showDeprecation>
                    </configuration>
                </plugin>-->

          <!--      <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>2.1.1</version>
                    <configuration>
                        <webXml>src/main/webapp\WEB-INF\web.xml</webXml>
                        <warSourceDirectory>src/main/webapp</warSourceDirectory>
                    </configuration>
                </plugin>
-->
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.2</version>
                    <configuration>
                        <!-- 指定端口 -->
                        <port>8080</port>
                        <!-- 请求路径 -->
                        <path>/shiro</path>
                        <uriEncoding>UTF-8</uriEncoding>
                    </configuration>
                </plugin>

            </plugins>
        </pluginManagement>
        <!-- 部署时需要携带配置文件 -->
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                    <!-- c -->
                    <include>**/*.tld</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                    <include>**/*.tld</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
</project>
2.web.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--   <listener>
          <listener-class>com.oracle.listener.CommonListener</listener-class>
      </listener> -->

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:spring/spring-root.xml
            classpath:spring/spring-shiro.xml
        </param-value>
    </context-param>

    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <!--将shiro过滤交给spring管理-->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:spring/spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
3.Spring集成shiro核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.1.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
	
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<!--注入安全管理器-->
		<property name="securityManager" ref="securityManager" />
		<!--登陆的url-->
		<property name="loginUrl" value="/admin/login.jsp" />
		<!--登录成功后的url-->
		<property name="successUrl" value="/admin/common/main" />
		<!--未授权的url-->
		<property name="unauthorizedUrl" value="/admin/common/unauthorized" />
		<!--配置授权地址列表-->
		<property name="filterChainDefinitions">
			<value>
			
				/ = authc
				/admin/login.jsp = authc
				/admin/logout.jsp = logout
				/admin/product/list = perms["admin:product:list"]
				/admin/product/add = perms["admin:product:add"]
				/admin/product/update = perms["admin:product:update"]
				/admin/product/delete = perms["admin:product:delete"]
				/admin/product/view = perms["admin:product:view"]
				/admin/category/list = perms["admin:category:list"]
				/admin/category/add = perms["admin:category:add"]
				/admin/category/update = perms["admin:category:update"]
				/admin/category/delete = perms["admin:category:delete"]
				/admin/category/view = perms["admin:category:view"]
				/admin/** = authc
			</value>
		</property>
		<property name="filters">
			<map>
				<entry key="authc" value-ref="authenticationFilter" />
				<entry key="logout" value-ref="logoutFilter" />
			</map>
		</property>
	</bean>
	
	<!--配置ehcache缓存管理器-->
	<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
		<property name="cacheManagerConfigFile" value="classpath:spring/shiro-cache.xml"/>
	</bean>	
	
	<!--登出跳转的url-->
	<bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter">
        <property name="redirectUrl" value="/admin/login.jsp" />
    </bean> 
    
    <!--安全管理器-->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="authenticationRealm" />
		<property name="cacheManager" ref="cacheManager"></property>
	</bean>


	<!--自定义认证-->
	<bean id="authenticationRealm" class="com.li.shiro.AuthenticationRealm">
	</bean>


	<bean id="authenticationFilter" class="com.li.filter.AuthenticationFilter" />

 
	<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
		<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager" />
		<property name="arguments" ref="securityManager" />
	</bean>

</beans>
4.shiro集成ehcache缓存(本地缓存)

shiro默认会在每次进行权限url授权校验时查询数据库的权限列表,所以使用缓存可减少重复查询次数,默认使用defaultCache默认缓存策略,也可以通过Spring提供的cache注解在业务层方法上指定缓存策略,如果指定则使用的是Spring提供的而不是shiro默认集成的。

shiro-ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" dynamicConfig="false">
    <diskStore path="java.io.tmpdir"/>
    <!--授权信息缓存-->
    <cache name="authorizationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           overflowToDisk="false"
           statistics="true">
    </cache>
<!--身份信息缓存-->
    <cache name="authenticationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           overflowToDisk="false"
           statistics="true">
    </cache>
<!--session缓存-->
    <cache name="activeSessionCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           overflowToDisk="false"
           statistics="true">
    </cache>


    <!--
        name:缓存名称。
        maxElementsInMemory:缓存最大个数。
        eternal:对象是否永久有效,一但设置了,timeout将不起作用。
        timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
        timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
        overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
        diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
        maxElementsOnDisk:硬盘最大缓存个数。
        diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
        memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
        clearOnFlush:内存数量最大时是否清除。
    -->
    <defaultCache name="defaultCache"
                  maxElementsInMemory="10000"
                  eternal="false"
                  timeToIdleSeconds="600"
                  timeToLiveSeconds="600"
                  overflowToDisk="false"
                  maxElementsOnDisk="100000"
                  diskPersistent="false"
                  diskExpiryThreadIntervalSeconds="120"
                  memoryStoreEvictionPolicy="LRU"/>

</ehcache>
5.创建AuthenticationToKen自定义令牌类
package com.li.shiro;

import org.apache.shiro.authc.UsernamePasswordToken;

/**
 * 自定义令牌
 * @author zhaoran
	2017年12月31日
 *
 */
public class AuthenticationToKen extends UsernamePasswordToken{
    //验证码状态
	private boolean isValid;
	public AuthenticationToKen(String loginName,String password,boolean isValid) {
		super(loginName, password);
		this.isValid=isValid;
	}
	public boolean isValid() {
		return isValid;
	}
	public void setValid(boolean isValid) {
		this.isValid = isValid;
	}
	
	
}

6.封装Principal身份牌类
package com.li.shiro;

import java.io.Serializable;

//身份牌
public class Principal implements Serializable{
	private Integer id;
	private String loginName;
	
	public Principal() {}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getLoginName() {
		return loginName;
	}

	public void setLoginName(String loginName) {
		this.loginName = loginName;
	}

	public Principal(Integer id, String loginName) {
		super();
		this.id = id;
		this.loginName = loginName;
	}
}

7.AuthenticationFilter 认证过滤器

AuthenticationFilter.java



public class AuthenticationFilter extends FormAuthenticationFilter {

	@Override
	protected boolean onAccessDenied(ServletRequest req, ServletResponse res) throws Exception {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
		String requestType = request.getHeader("X-Requested-With");
		if (requestType != null && requestType.equalsIgnoreCase("XMLHttpRequest")) {
			response.addHeader("loginStatus", "accessDenied");
			response.sendError(HttpServletResponse.SC_FORBIDDEN);
			return false;
		}
		return super.onAccessDenied(request, response);
	}


	//获取登录信息,创建认证令牌
	@Override
	protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
		//默认为true,表示有验证码不为空
		boolean bln=true;
		//获取用户名和密码(前台name值必须为username)
		String loginName=getUsername(request);
		String password=getPassword(request);
		HttpServletRequest req=(HttpServletRequest) request;
		HttpSession session=req.getSession();
		//获取验证码
		String captCode=request.getParameter("code");
		String sessionCaptCode=(String) session.getAttribute("valcode");
		//判断验证码是否为空
		if((null==sessionCaptCode)||(null==captCode)) {
			bln=false;
		}
		//判断验证码是否相等
		if(sessionCaptCode!=null&&captCode!=null) {
			if(!sessionCaptCode.equals(captCode)) {
				bln=false;
			}
		}
		//返回自定义令牌
		return new AuthenticationToKen(loginName, password,bln);
	}

	@Override
	protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
		WebUtils.issueRedirect(request, response, getSuccessUrl());
		return false;
	}
}

8.AuthenticationRealm 身份认证授权
public class AuthenticationRealm extends AuthorizingRealm {
	
	@Resource
	private AdminService adminService;

	//利用身份牌进行授权
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//获取自定义身份牌
		Principal principal=(Principal) principals.fromRealm(getName()).iterator().next();
		if(principal!=null) {
			//获取授权的url集合
			List<String> listPer=this.adminService.getAdminUrl(principal.getId());
			if(listPer!=null) {
				//返回认证信息
				SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
				//添加授权url集合
				authorizationInfo.addStringPermissions(listPer);
				return authorizationInfo;
			}
		}
		return null;
	}

	
	//使用认证令牌信息进行登录认证
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		AuthenticationToKen authenticationToKen=(AuthenticationToKen) token;
		String loginName=authenticationToKen.getUsername();
		String password=new String(authenticationToKen.getPassword());
		//判断验证码,如果不存在抛异常
		if(!authenticationToKen.isValid()) {
			throw new UnsupportedTokenException();
		}
		//判断登录名是否为空
		if(loginName!=null&&!"".equals(loginName)) {
			//通过登录名获取用户对象
			Admins admin=this.adminService.getAdminByLoginName(loginName);
			//如果用户为空,抛异常
			if(admin==null) {
				throw new UnknownAccountException();
			}
			//判断密码是否正确
			if(!admin.getPwd().equals(password)) {
				throw new IncorrectCredentialsException();
			}
			//设置登录日期
			admin.setLogindate(new Date());
			//更新信息
			this.adminService.updateAdmin(admin);
			//返回认证信息,此处密码错误会抛异常,
			return new SimpleAuthenticationInfo(new Principal(admin.getId(), loginName), password, getName());
		}
		return null;
	}
}
9.登录页login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>
<%@page import="org.apache.shiro.web.filter.authc.FormAuthenticationFilter"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>

<shiro:authenticated>
<%
response.sendRedirect(request.getContextPath()+"/admin/common/main");
%>
</shiro:authenticated>

<%--判断抛出的异常对用户进行提示--%>
<%
String loginError = (String) request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);
	if(loginError!=null){
		if(loginError.equals("org.apache.shiro.authc.pam.UnsupportedTokenException")){
			pageContext.setAttribute("loginError", "验证码错误");
		}else if(loginError.equals("org.apache.shiro.authc.UnknownAccountException")){
			pageContext.setAttribute("loginError", "用户名不存在或密码不正确");
		}else if(loginError.equals("org.apache.shiro.authc.IncorrectCredentialsException")){
			pageContext.setAttribute("loginError", "用户名不存在或密码不正确");
		}
	}
%>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<link rel="stylesheet" href="${root}/resources/css/pintuer.css">
    <link rel="stylesheet" href="${root}/resources/css/admin.css">
    <script src="${root}/resources/js/jquery.js"></script>
    <script src="${root}/resources/js/pintuer.js"></script>
<title>管理员登录</title>
</head>
<body>
	<div class="bg"></div>
<div class="container">
    <div class="line bouncein">
        <div class="xs6 xm4 xs3-move xm4-move">
            <div style="height:150px;"></div>
            <div class="media media-y margin-big-bottom">           
            </div>         
            <form action="login.jsp" method="post">
            <div class="panel loginbox">
                <div class="text-center margin-big padding-big-top"><h1>后台管理中心</h1></div>
                <div class="panel-body" style="padding:30px; padding-bottom:10px; padding-top:10px;">
                    <div class="form-group">
                        <div class="field field-icon-right">
                            <input type="text" class="input input-big" name="username" placeholder="登录账号" data-validate="required:请填写账号" />
                            <span class="icon icon-user margin-small"></span>
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="field field-icon-right">
                            <input type="password" class="input input-big" name="password" placeholder="登录密码" data-validate="required:请填写密码" />
                            <span class="icon icon-key margin-small"></span>
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="field">
                            <input type="text" class="input input-big" name="code" placeholder="填写右侧的验证码" data-validate="required:请填写右侧的验证码" />
                           <img src="${root}/captServlet" alt="" width="100" height="32" class="passcode" style="height:43px;cursor:pointer;" οnclick="this.src=this.src+'?'">
                                                   
                        </div>
                    </div>
                </div>
                <span style="color: red;font-size: 12px;">${loginError}</span>
                <div style="padding:30px;"><input type="submit" class="button button-block bg-main text-big input-big" value="登录"></div>
            </div>
            </form>          
        </div>
    </div>
</div>
</body>
</html>
10.主页显示判断
   <%--判断授权地址--%>
  <shiro:hasPermission name="admin:product:view">
    <h2><span class="icon-pencil-square-o"></span>产品管理</h2>
  <ul>
    <li><a href="${root}/admin/product/list" target="right"><span class="icon-caret-right"></span>浏览产品</a></li>
    <li><a href="${root}/admin/product/update" target="right"><span class="icon-caret-right"></span>修改产品</a></li>
  </ul>
   </shiro:hasPermission> 

2.SpringBoot+thymeleaf集成Shiro

自定义认证过滤验证码

需要在核心配置类手动配置过滤的类 filters.put(“authc”, new AuthenticationFilter());

用户登录时,会走自定义过滤器由shiro进行登录操作,成功后通过配置类跳转页面,失败后则会抛出异常,由控制层登录方法获取shiro异常进行页面输出。

注意:用户认证成功后,再进登录页无法重新登录,而是会直接走控制层登录方法,session未清除前,只能认证一次,所以登录成功后不能进行登录操作,应先退出登录清除session。

需在核心配置类添加方言,否则页面标签不生效

3-6步同上方整合spring

1.pom坐标
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.li</groupId>
    <artifactId>springboot-shiro</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-shiro</name>

    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


        <!-- thymeleaf模板引擎:jsp就是一个模板引擎 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>

        <!--  热部署:修改完java代码后,tomcat会自动重启 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>

        <!-- MySQL的JDBC驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>


        <!--shiro start-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
        </dependency>

        <!--
        第三方数据源:C3P0,DBCP,Druid
        -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>


    </dependencies>

    <!-- 构建,编译 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.5</version>
                <configuration>
                    <fork>true</fork>
                </configuration>
            </plugin>
        </plugins>

        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>

2.SpringBoot中的Shiro核心配置类
/**
 * shiro的核心配置类
 * 1.配置自定义的Realm类,需要创建realm对象
 * 2.配置安全管理器
 * 3.配置shiroFilterFactoryBean对象
 */
@Configuration
public class ShiroConfig {

    //配置自定义Realm注册为bean
    @Bean
    public Realm authenticationRealm(){
        return new AuthenticationRealm();
    }


    //配置安全管理器
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(Realm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //设置自定义的Realm
        securityManager.setRealm(realm);
        return securityManager;
    }

    //配置shiroFilterFactoryBean对象
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();//获取filters
        //将自定义 的FormAuthenticationFilter注入shiroFilter中
        filters.put("authc", new AuthenticationFilter());
        //拦截器----Map集合
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        /*
         * 使用Shiro内置过滤器实现页面拦截:拦截url链接请求
         *
         * shiro内置过滤器,可以实现权限相关的拦截器
         *      常用的过滤器:
         *          anon:无需认证(登录)可以访问
         *          authc:必须认证才可以访问
         *          user:如果使用rememberMe的功能可以直接访问  (记住用户和密码)
         *          perms:该资源必须得到资源权限才可以访问      (密码验证)
         *          role:该资源必须得到角色权限才可以访问       (VIP会员)
         *
         */
        //表示这个为公共资源 一定是在受限资源上面
        filterChainDefinitionMap.put("/capt", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");
        //根据用户角色赋予相应的权限
       /* filterChainDefinitionMap.put("/add-success.html", "roles[Commom]");
        filterChainDefinitionMap.put("/selete-success.html", "roles[Commom]");
        filterChainDefinitionMap.put("/update-success.html", "roles[Member]");
        filterChainDefinitionMap.put("/delete-success.html", "roles[Vip]");*/
        //根据用户拥有的具体权限赋予相应的权限
        filterChainDefinitionMap.put("/admin/product/list", "perms[admin:product:list]");
        filterChainDefinitionMap.put("/admin/product/add", "perms[admin:product:add]");
        filterChainDefinitionMap.put("/admin/product/update", "perms[admin:product:update]");
        filterChainDefinitionMap.put("/admin/product/delete", "perms[admin:product:delete]");
        filterChainDefinitionMap.put("/admin/product/view", "perms[admin:product:view]");
        filterChainDefinitionMap.put("/admin/category/list", "perms[admin:category:list]");
        filterChainDefinitionMap.put("/admin/category/add", "perms[admin:category:add]");
        filterChainDefinitionMap.put("/admin/category/update", "perms[admin:category:update]");
        filterChainDefinitionMap.put("/admin/category/delete", "perms[admin:category:delete]");
        filterChainDefinitionMap.put("/admin/category/view", "perms[admin:category:view]");

        //   /** 匹配所有的路径
        //  通过Map集合组成了一个拦截器链 ,自顶向下过滤,一旦匹配,则不再执行下面的过滤
        //  如果下面的定义与上面冲突,那按照了谁先定义谁说了算
        //  /** 一定要配置在最后
        filterChainDefinitionMap.put("/**", "authc");
        // 将拦截器链设置到shiro中
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面,没有认证时拦截跳转
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/admin/common/main");
        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/admin/common/unauthorized");

        return shiroFilterFactoryBean;
    }


    /**
     * 开启shiro aop注解支持
     * 使用代理方式;所以需要开启代码支持
     * @param securityManager
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    /**
     * 开启cglib代理
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;

    }

    //加方言,否则标签不生效
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }
}
3.创建AuthenticationToKen自定义令牌类
package com.li.shiro;

import org.apache.shiro.authc.UsernamePasswordToken;

/**
 * 自定义令牌
 * @author zhaoran
	2017年12月31日
 *
 */
public class AuthenticationToKen extends UsernamePasswordToken{
    //验证码状态
	private boolean isValid;
	public AuthenticationToKen(String loginName,String password,boolean isValid) {
		super(loginName, password);
		this.isValid=isValid;
	}
	public boolean isValid() {
		return isValid;
	}
	public void setValid(boolean isValid) {
		this.isValid = isValid;
	}
	
	
}

4.封装Principal身份牌类
package com.li.shiro;

import java.io.Serializable;

//身份牌
public class Principal implements Serializable{
	private Integer id;
	private String loginName;
	
	public Principal() {}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getLoginName() {
		return loginName;
	}

	public void setLoginName(String loginName) {
		this.loginName = loginName;
	}

	public Principal(Integer id, String loginName) {
		super();
		this.id = id;
		this.loginName = loginName;
	}
}

5.AuthenticationFilter 认证过滤器

AuthenticationFilter.java



public class AuthenticationFilter extends FormAuthenticationFilter {

	@Override
	protected boolean onAccessDenied(ServletRequest req, ServletResponse res) throws Exception {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
		String requestType = request.getHeader("X-Requested-With");
		if (requestType != null && requestType.equalsIgnoreCase("XMLHttpRequest")) {
			response.addHeader("loginStatus", "accessDenied");
			response.sendError(HttpServletResponse.SC_FORBIDDEN);
			return false;
		}
		return super.onAccessDenied(request, response);
	}


	//获取登录信息,创建认证令牌
	@Override
	protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
		//默认为true,表示有验证码不为空
		boolean bln=true;
		//获取用户名和密码(前台name值必须为username)
		String loginName=getUsername(request);
		String password=getPassword(request);
		HttpServletRequest req=(HttpServletRequest) request;
		HttpSession session=req.getSession();
		//获取验证码
		String captCode=request.getParameter("code");
		String sessionCaptCode=(String) session.getAttribute("valcode");
		//判断验证码是否为空
		if((null==sessionCaptCode)||(null==captCode)) {
			bln=false;
		}
		//判断验证码是否相等
		if(sessionCaptCode!=null&&captCode!=null) {
			if(!sessionCaptCode.equals(captCode)) {
				bln=false;
			}
		}
		//返回自定义令牌
		return new AuthenticationToKen(loginName, password,bln);
	}

	@Override
	protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
		WebUtils.issueRedirect(request, response, getSuccessUrl());
		return false;
	}
}

6.AuthenticationRealm 身份认证授权
public class AuthenticationRealm extends AuthorizingRealm {
	
	@Resource
	private AdminService adminService;

	//利用身份牌进行授权
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//获取自定义身份牌
		Principal principal=(Principal) principals.fromRealm(getName()).iterator().next();
		if(principal!=null) {
			//获取授权的url集合
			List<String> listPer=this.adminService.getAdminUrl(principal.getId());
			if(listPer!=null) {
				//返回认证信息
				SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
				//添加授权url集合
				authorizationInfo.addStringPermissions(listPer);
				return authorizationInfo;
			}
		}
		return null;
	}

	
	//使用认证令牌信息进行登录认证
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		AuthenticationToKen authenticationToKen=(AuthenticationToKen) token;
		String loginName=authenticationToKen.getUsername();
		String password=new String(authenticationToKen.getPassword());
		//判断验证码,如果不存在抛异常
		if(!authenticationToKen.isValid()) {
			throw new UnsupportedTokenException();
		}
		//判断登录名是否为空
		if(loginName!=null&&!"".equals(loginName)) {
			//通过登录名获取用户对象
			Admins admin=this.adminService.getAdminByLoginName(loginName);
			//如果用户为空,抛异常
			if(admin==null) {
				throw new UnknownAccountException();
			}
			//判断密码是否正确
			if(!admin.getPwd().equals(password)) {
				throw new IncorrectCredentialsException();
			}
			//设置登录日期
			admin.setLogindate(new Date());
			//更新信息
			this.adminService.updateAdmin(admin);
			//返回认证信息,此处密码错误会抛异常,
			return new SimpleAuthenticationInfo(new Principal(admin.getId(), loginName), password, getName());
		}
		return null;
	}
}
7.LoginController登录控制层
/**
 * 功能描述:LoginController
 * @author https://blog.csdn.net/chen_2890
 * @date 2018/12/5 19:41:01
 */
@Controller
public class LoginController {

    /**
     * 描述:session对象
     * @date 2018/12/9 20:35
     */
    @Autowired
    private HttpSession session;

    /**
     * 功能描述:login,即shiro的认证
     * @return org.springframework.http.ResponseEntity<java.lang.Void>
     * @author https://blog.csdn.net/chen_2890
     * @date 2018/12/9 20:35
     **/
    @RequestMapping("/login")
    public String login(HttpServletRequest request, Model model){
        System.out.println("HomeController.login");
        // 登录失败从request中获取shiro处理的异常信息
        String loginError = (String) request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);
        if(loginError!=null){
            if(loginError.equals("org.apache.shiro.authc.pam.UnsupportedTokenException")){
                model.addAttribute("loginError", "验证码错误");
            }else if(loginError.equals("org.apache.shiro.authc.UnknownAccountException")){
                model.addAttribute("loginError", "用户名不存在或密码不正确");
            }else if(loginError.equals("org.apache.shiro.authc.IncorrectCredentialsException")){
                model.addAttribute("loginError", "用户名不存在或密码不正确");
            }
        }
        return "admin/comm/login";
    }

    @RequestMapping("/toLogin")
    public String toLogin(){
        return "admin/comm/login";
    }

    /**
     * 功能描述: shiro的logout退出,只是将放置PrincipalCollection这个集合置空,
     * 删除了session,但是没有清空缓存,需要手动清除缓存
     **/
    @PutMapping("/logout")
    public ResponseEntity<Void> logout(){
        //清空session
        session.removeAttribute("loginUser");
        //退出shiro
        SecurityUtils.getSubject().logout();
        //返回状态
        return new ResponseEntity<>(HttpStatus.OK);
    }

    /**
     * 验证码
     * @param response
     * @param session
     * @throws IOException
     */
    @RequestMapping("/capt")
    public void captChange(HttpServletResponse response, HttpSession session) throws IOException {

        response.setContentType("image/jpeg");

        response.setHeader("pragma", "no-cache");

        response.setHeader("cache-control", "no-cache");

        response.setHeader("expires", "0");


        int length = 4;

        String valcode = "";

        Random rd = new Random();

        for (int i = 0; i < length; i++)

            valcode += rd.nextInt(10);

        session.setAttribute("valcode", valcode);


        int width = 80;

        int height = 25;

        BufferedImage img = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);


        Graphics g = img.getGraphics();


        g.setColor(Color.WHITE);

        g.fillRect(0, 0, width, height);


        for (int i = 0; i < 50; i++) {

            g.setColor(new Color(rd.nextInt(100) + 155, rd.nextInt(100) + 155,
                    rd.nextInt(100) + 155));

            g.drawLine(rd.nextInt(width), rd.nextInt(height),
                    rd.nextInt(width), rd.nextInt(height));

        }


        g.setColor(Color.GRAY);

        g.drawRect(0, 0, width - 1, height - 1);


        Font[] fonts = { new Font("隶书", Font.BOLD, 18),
                new Font("楷体", Font.BOLD, 18), new Font("宋体", Font.BOLD, 18),
                new Font("幼圆", Font.BOLD, 18) };

        for (int i = 0; i < length; i++) {

            g.setColor(new Color(rd.nextInt(150), rd.nextInt(150), rd
                    .nextInt(150)));

            g.setFont(fonts[rd.nextInt(fonts.length)]);

            g.drawString(valcode.charAt(i) + "", width / valcode.length() * i
                    + 2, 18);

        }
        g.dispose();
        ImageIO.write(img, "jpeg", response.getOutputStream());
    }
}

8.登录页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" >
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>

<form action="/login" method="post">
    <div class="panel loginbox">
        <div class="text-center margin-big padding-big-top"><h1>后台管理中心</h1></div>
        <div class="panel-body" style="padding:30px; padding-bottom:10px; padding-top:10px;">
            <div class="form-group">
                <div class="field field-icon-right">
                    <input type="text" class="input input-big" name="username" placeholder="登录账号" data-validate="required:请填写账号" />
                    <span class="icon icon-user margin-small"></span>
                </div>
            </div>
            <div class="form-group">
                <div class="field field-icon-right">
                    <input type="password" class="input input-big" name="password" placeholder="登录密码" data-validate="required:请填写密码" />
                    <span class="icon icon-key margin-small"></span>
                </div>
            </div>
            <div class="form-group">
                <div class="field">
                    <input type="text" class="input input-big" name="code" placeholder="填写右侧的验证码" data-validate="required:请填写右侧的验证码" />
                    <img src="/capt" alt="" width="100" height="32" class="passcode" style="height:43px;cursor:pointer;" onclick="this.src=this.src+'?'">

                </div>
            </div>
        </div>
        <span style="color: red;font-size: 12px;" th:text="${loginError}"></span>
        <div style="padding:30px;"><input type="submit" class="button button-block bg-main text-big input-big" value="登录"></div>
    </div>
</form>
</body>
</html>
9.主页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <meta charset="UTF-8">
    <title>主页面</title>
</head>
<body>

<div shiro:hasPermission="admin:product:view">
    <h2><span class="icon-pencil-square-o"></span>产品管理</h2>
    <ul>
        <li><a href="/admin/product/list" target="right"><span class="icon-caret-right"></span>浏览产品</a></li>
        <li><a href="/admin/product/update" target="right"><span class="icon-caret-right"></span>修改产品</a></li>
    </ul>
</div>

<div shiro:hasPermission="admin:category:view">
    <h2><span class="icon-pencil-square-o"></span>分类管理</h2>
    <ul>
        <li><a href="/admin/category/list" target="right"><span class="icon-caret-right"></span>浏览分类</a></li>
        <li><a href="/admin/category/add" target="right"><span class="icon-caret-right"></span>添加分类</a></li>
    </ul>
</div>


<div shiro:hasPermission="admin:role">
    <h2><span class="icon-pencil-square-o"></span>系统管理</h2>
    <ul>
        <li><a href="/admin/role/list" target="right"><span class="icon-caret-right"></span>浏览角色</a></li>
        <li><a href="/admin/admin/list" target="right"><span class="icon-caret-right"></span>管理员</a></li>
    </ul>
</div>

</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值