官方文档
What is Apache Shiro?
Apache Shiro is a powerful and easy to use Java security framework that offers developers an intuitive yet comprehensive solution to authentication, authorization, cryptography, and session management.
In practical terms, it achieves to manage all facets of your application’s security, while keeping out of the way as much as possible. It is built on sound interface-driven design and OO principles, enabling custom behavior wherever you can imagine it. But with sensible defaults for everything, it is as "hands off" as application security can be. At least that’s what we strive for.
什么是Apache Shiro?
Apache Shiro 是一个强大和易于使用的Java安全框架,为开发者提供了一个易懂而又全面的身份认证、授权、加密和会话管理的解决方案。
实际上,它实现了管理你的应用安全的所有方面,同时尽可能避免了后门的存在。它建立在良好的界面驱动设计和面向对象原则之上,允许在你任何能想象到的地方使用自定义的行为。但是所有都使用默认的设置,它就像应用安全可以的那样“放开手”。至少那是我们努力的目标。
Terminology you’ll need
Subject
Security specific user 'view' of an application user. It can be a human being, a third-party process, a server connecting to your application, or even a cron job. Basically, it is anything or anyone communicating with your application.
Principals
A subjects identifying attributes. First name, last name, social security number, username
Credentials
secret data that are used to verify identities. Passwords, Biometric data, x509 certificates
Realms
Security specific DAO, data access object, software component that talks to a backend data source. If you have usernames and password in LDAP, then you would have an LDAP Realm that would communicate with LDAP. The idea is that you would use a realm per back-end data source and Shiro would know how to coordinate with these realms together to do what you have to do.
你需要知道术语
Subject
一个应用用户的安全特定用户视图。它可以是一个人,一个第三方进程,一个连接到你的应用的服务,或者设置是一个定时任务。基本上,它是与你的应用进行通信的任何东西或者任何人。
Principals
一个主体标识属性。名字、姓氏、社保号码、用户名。
Credentials
用于验证身份的密钥。密码、生物特征数据、x509证书。
Realms
安全特定的DAO服务,数据访问对象,与后端数据源通信的软件组件。如果你有LDAP的用户名和密码,那么你将有一个可以与LDAP通信的LDAP Realm。这个想法是你将为每个后端数据源使用一个Reaml,Shiro知道怎么协调处理这些realms在一起,做你必须要做的事。
- 新建一个基于 Maven 的"webapp"模板的基础工程
- 在 main 文件夹下新建
java
源码文件夹
- 添加Spring MVC框架所需的POM配置
<properties>
<spring.version>5.1.5.RELEASE</spring.version>
<jackson.version>2.9.8</jackson.version>
<shiro.version>1.3.2</shiro.version>
</properties>
<dependencies>
<!-- slf4j-nop -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.2</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- spring core 核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring bean bean的管理 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring context support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring mvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring El表达式 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- json包jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- json包jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- json包jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.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-web</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-ehcache</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- shiro的依赖结束 -->
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>webapp-shiro</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>8080</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
<server>tomcat7</server>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
- 新建jdbc.properties配置文件(
resources
文件夹下)
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_shiro
jdbc.username=root
jdbc.password=root
- 新建spring-context.xml配置文件(
resources
文件夹下)
<?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:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">
<!-- 开启注解扫描,对包中的所有类进行扫描,以完成Bean创建和自动依赖注入的功能 -->
<context:component-scan base-package="com.shiro" />
<!-- 开启注解驱动,启动基于Spring MVC的注解功能,将控制器与方法映射加入到容器中 -->
<mvc:annotation-driven />
<!-- 配置Spring MVC视图解析器 -->
<bean id="ViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
- 新建spring-mybatis.xml配置文件(
resources
文件夹下)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 读取数据库连接的配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 开启注解扫描,并开启注解驱动,用于测试 -->
<!-- <context:component-scan base-package="com.shiro" />
<context:annotation-config /> -->
<!-- 创建数据源,连接数据库 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 创建sqlSessionFactory工厂,在工厂中依赖数据源,将数据源映射到sqlSessionFactory中 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 配置别名,不用在mybatis-config.xml配置文件中配置别名 -->
<property name="typeAliasesPackage" value="com.shiro.entity" />
<!-- ref中的dataSource是<bean id="dataSource">创建数据源,连接数据库的id值 -->
<property name="dataSource" ref="dataSource" />
<!-- 获得所有的mapper文件,自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:mappers/*.xml" />
</bean>
<!-- 配置dao接口所在的包。DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 得到所有的dao接口文件,与mapper文件进行对应的映射 -->
<property name="basePackage" value="com.shiro.dao" />
<!-- 依赖sqlSessionFactory工厂 -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!-- 事务管理器(由Spring管理MyBatis的事务) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 关联数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
- 新建spring-shiro.xml配置文件(
resources
文件夹下)
<?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">
<!-- 項目自定义的Realm,从数据库中获取用户的安全数据 -->
<bean id="myShiroRealm" class="com.shiro.utils.MyRealm" />
<!-- 用户授权信息Cache(缓存) -->
<bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
<!-- 配置安全管理器securityManager, 缓存技术: 缓存管理 realm:负责获取处理数据 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myShiroRealm" />
<property name="cacheManager" ref="cacheManager" />
</bean>
<!-- shiro的核心配置(自定义权限校验): 配置shiroFileter id名必须与web.xml中的filtername保持一致 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager" />
<!-- 身份认证失败,跳转到登录页面的配置 -->
<property name="loginUrl" value="/user/login" />
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="successUrl" value="/user/list" />
<!-- 权限认证失败,跳转到指定页面 -->
<property name="unauthorizedUrl" value="/unauthorized.jsp" />
<!-- Shiro连接约束配置,即过滤链的定义 -->
<property name="filterChainDefinitions">
<value>
<!--anon 表示匿名访问,不需要认证以及授权 -->
/user/login = anon
/user/dologin = anon
/user/list = authc
<!-- /api/** = anon -->
<!-- 表示访问/admin请求的用户必须是admin角色,不然是不能进行访问的 -->
<!-- /admin/** = roles[admin] -->
<!-- 表示访问/teacher请求是需要当前用户具有user:create权限才能进行访问的 -->
<!-- /teacher=perms["user:create"] -->
/user/loginOut=logout <!-- 退出登录 -->
<!--authc表示需要认证 没有进行身份认证是不能进行访问的,必须放在最后 -->
/** = authc
</value>
</property>
</bean>
<!-- 必须配置lifecycleBeanPostProcessor为了管理shiro中常见的对象 -->
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!-- 开启Shiro注解 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor" />
</beans>
- 修复web.xml配置文件(
webapp/WEB-INFO
文件夹下)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<!-- 配置上下文路径参数 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-*.xml</param-value>
</context-param>
<!-- 配置上下文监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>shiro</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 设置spring-context.xml的路径 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-context.xml</param-value>
</init-param>
<!-- 1表示启动项目就加载这个类 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>shiro</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>
<!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->
<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>
<!-- 设置字符编码格式过滤器 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<!-- 设置编码格式为UTF-8 -->
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<!-- 强制修改字符编码 -->
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<!-- 所有请求都要经过该过滤器 -->
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
- 新建userMapper.xml文件(
resources/mappers
文件夹下)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace用接口的全路径作为命名空间 -->
<mapper namespace="com.shiro.dao.UserDao">
<!-- 查询用户全部信息 -->
<select id="list" resultType="User">
select * from t_user
</select>
<!-- 根据用户名来查询用户信息 -->
<select id="findUserByUsername" parameterType="java.lang.String" resultType="User">
select * from t_user where username=#{username}
</select>
<!-- 根据用户名来查询角色信息 -->
<select id="findRoles" parameterType="java.lang.String" resultType="java.lang.String">
select r.role_name from
t_user u,t_role r,t_user_role ur
where u.id = ur.userid
and r.role_id = ur.roleid
and u.username = #{username}
</select>
<!-- 根据用户名来查询权限信息 -->
<select id="findPermissions" parameterType="java.lang.String" resultType="java.lang.String">
select p.permission_name from
t_user u,t_role r,t_permission p,t_user_role ur,t_role_permission rp
where u.id = ur.userid
and r.role_id = ur.roleid
and r.role_id = rp.roleid
and rp.permissionid = p.permission_id
and u.username = #{username}
</select>
</mapper>
- 新建Md5Util加密工具类
package com.shiro.utils;
import org.apache.shiro.crypto.hash.Md5Hash;
public class Md5Util {
// 基于shiro的MD5加密,加密结果为32位小写
public static String md5(String str) {
return new Md5Hash(str).toString();
}
}
- 新建MyRealm工具类
package com.shiro.utils;
import java.util.Set;
import javax.annotation.Resource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.ByteSource;
import com.shiro.entity.User;
import com.shiro.service.UserService;
public class MyRealm extends AuthorizingRealm {
@Resource
private UserService userService;
// 用户授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = principals.getPrimaryPrincipal().toString();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> roleName = userService.findRoles(username);
Set<String> permissions = userService.findPermissions(username);
info.setRoles(roleName);
info.setStringPermissions(permissions);
return info;
}
// 登录认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 获取用户账号
String username = token.getPrincipal().toString();
User user = userService.findUserByUsername(username);
if (user != null) {
// 如果查询到了,封装查询结果
Object principal = user.getUsername();
Object credentials = user.getPassword();
String realmName = this.getName();
String salt = user.getUsername();
ByteSource byteSalt = ByteSource.Util.bytes(salt);// 根据用户名加盐
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(principal, credentials, byteSalt,
realmName);
return authenticationInfo;
} else {
return null;
}
}
// 清空当前用户权限信息
public void clearCachedAuthorizationInfo() {
PrincipalCollection principalCollection = SecurityUtils.getSubject().getPrincipals();
SimplePrincipalCollection principals = new SimplePrincipalCollection(principalCollection, getName());
super.clearCachedAuthorizationInfo(principals);
}
// 指定清除principalCollection
public void clearCachedAuthorizationInfo(PrincipalCollection principalCollection) {
SimplePrincipalCollection principals = new SimplePrincipalCollection(principalCollection, getName());
super.clearCachedAuthorizationInfo(principals);
}
}
- 新建User实体
package com.shiro.entity;
public class User {
private int id;
private String username;
private String password;
private String phone;
private String nickname;
private String email;
public User() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + ", phone=" + phone
+ ", nickname=" + nickname + ", email=" + email + "]";
}
}
- 新建UserDao层
package com.shiro.dao;
import java.util.List;
import java.util.Set;
import org.springframework.stereotype.Repository;
import com.shiro.entity.User;
@Repository
public interface UserDao {
// 查询用户全部信息
List<User> list();
// 根据用户名来查询用户信息
User findUserByUsername(String username);
// 根据用户名来查询角色信息
Set<String> findRoles(String username);
// 根据用户名来查询权限信息
Set<String> findPermissions(String username);
}
- UserService接口
package com.shiro.service;
import java.util.List;
import java.util.Set;
import com.shiro.entity.User;
public interface UserService {
// 查询用户全部信息
List<User> list();
// 根据用户名来查询用户信息
User findUserByUsername(String username);
// 根据用户名来查询角色信息
Set<String> findRoles(String username);
// 根据用户名来查询权限信息
Set<String> findPermissions(String username);
}
- 新建UserServiceImpl实现类
package com.shiro.service.impl;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.shiro.dao.UserDao;
import com.shiro.entity.User;
import com.shiro.service.UserService;
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public List<User> list() {
return userDao.list();
}
@Override
public User findUserByUsername(String username) {
return userDao.findUserByUsername(username);
}
@Override
public Set<String> findRoles(String username) {
return userDao.findRoles(username);
}
@Override
public Set<String> findPermissions(String username) {
return userDao.findPermissions(username);
}
}
- 新建UserController层
package com.shiro.controller;
import java.util.List;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.shiro.entity.User;
import com.shiro.service.UserService;
import com.shiro.utils.Md5Util;
@Controller
@RequestMapping("user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("login")
public String login() {
return "login";
}
@PostMapping("dologin")
@ResponseBody
public boolean dologin(User user, boolean rememberMe) {
boolean loginflag = false;
// 创建Subject实例对象
Subject currentUser = SecurityUtils.getSubject();
// 判断当前用户是否已登录
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),
Md5Util.md5(user.getPassword()));
System.out.println(token);
boolean flag = false;
try {
token.setRememberMe(rememberMe);
currentUser.login(token);
loginflag = true;
} catch (DisabledAccountException dax) {
System.out.println("用户名为:" + dax.getMessage() + " 用户已经被禁用!");
flag = true;
} catch (ExcessiveAttemptsException eae) {
System.out.println("用户名为:" + eae + " 用户登录次数过多,有暴力破解的嫌疑!");
flag = true;
} catch (AuthenticationException ae) {
System.out.println("------------------身份认证失败-------------------");
flag = true;
} catch (Exception e) {
System.out.println("未知异常信息。。。。");
flag = true;
}
if (flag) {
return loginflag;
}
}
return loginflag;
}
@GetMapping("list")
public String list(Model model) {
List<User> users = userService.list();
model.addAttribute("users", users);
return "user/list";
}
@GetMapping("loginOut")
public String loginOut() {
return "redirect:/user/login";
}
}
- 新建login.jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.Date" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<% pageContext.setAttribute("date", new Date()); %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录页面</title>
<link rel="stylesheet" type="text/css" href="https://www.layuicdn.com/layui-v2.5.3/css/layui.css" />
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script src="https://www.layuicdn.com/layer-v3.1.0/layer.js"></script>
</head>
<body>
<div class="layui-row layui-col-space5">
<div class="layui-col-md4">
<div class="grid-demo grid-demo-bg1"></div>
</div>
<div class="layui-col-md4">
<div class="grid-demo">
<fieldset class="layui-elem-field layui-field-title"
style="margin-top: 50px;">
<legend>用户登录</legend>
<span style="padding-left: 30px;color: pink;">
<fmt:formatDate value="${date}" pattern="yyyy年MM月dd日"/>
</span>
</fieldset>
<form>
<span id="msg" style="margin-left: 18%; color: red"></span>
<div>
<div class="layui-form-item">
<label class="layui-form-label">用户名:</label>
<div class="layui-input-block">
<input type="text" name="username" id="username" lay-verify="title" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">密 码:</label>
<div class="layui-input-block">
<input type="password" name="password" id="password" lay-verify="title" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button type="button" id="login"
class="layui-btn layui-btn-lg layui-btn-primary layui-btn-radius">立即登录</button>
<button style="float: right" type="button" id="register"
class="layui-btn layui-btn-lg layui-btn-primary layui-btn-radius">没有账号?</button>
</div>
</div>
</div>
</form>
</div>
</div>
<div class="layui-col-md4">
<div class="grid-demo grid-demo-bg1"></div>
</div>
</div>
<script type="text/javascript">
$(function() {
$("#login").on("click", function() {
var username = $("#username").val();
var password = $("#password").val();
if(!username) {
$("#msg").html("用户名不能为空!");
$("#username").focus();
return false;
} else {
if(!password) {
$("#msg").html("密码不能为空!");
$("#password").focus();
return false;
} else {
$("#msg").html("");
}
}
$.ajax({
type: "post", // post或get
url: "${pageContext.request.contextPath}/user/dologin", // 提交路径
data: {
username: username,
password: password
},
success: function(data) {
console.log(data);
if (data == true) {
layer.msg('登录中', {
icon: 16
,shade: 0.01
});
// 延迟跳转
window.setTimeout("window.location='${pageContext.request.contextPath}/user/list'",1000);
} else {
layer.tips('用户名或密码错误!', '#login', {
tips: [2, '#3595CC'],
time: 2000
});
}
},
error: function(err) {
layer.msg('数据异常!');
}
})
});
$("#register").on("click", function() {
layer.msg('数据异常!');
});
})
</script>
<!-- <script>
// 防止点击页面后退
history.pushState(null, null, document.URL);
window.addEventListener('popstate', function() {
history.pushState(null, null, document.URL);
});
</script> -->
</body>
</html>
- 新建user的list.jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>用户列表</title>
</head>
<body>
欢迎[ <shiro:principal/> ]登录用户列表
<a href="${pageContext.request.contextPath}/user/loginOut">退出</a>
<hr>
<c:forEach items="${users}" var="user">
${user.id} ----> ${user.username} ----> ${user.phone} ----> ${user.email}<br>
</c:forEach>
</body>
</html>
- 创建数据库
-- MySQL dump 10.13 Distrib 8.0.19, for Win64 (x86_64)
--
-- Host: localhost Database: db_shiro
-- ------------------------------------------------------
-- Server version 5.7.44-log
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Database structure for Database `db_shiro`
--
CREATE DATABASE `db_shiro` /*!40100 DEFAULT CHARACTER SET utf8 */;
--
-- Table structure for table `t_permission`
--
DROP TABLE IF EXISTS `t_permission`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `t_permission` (
`permission_id` bigint(20) NOT NULL AUTO_INCREMENT,
`permission_name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`permission_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `t_permission`
--
LOCK TABLES `t_permission` WRITE;
/*!40000 ALTER TABLE `t_permission` DISABLE KEYS */;
INSERT INTO `t_permission` VALUES (1,'1111');
/*!40000 ALTER TABLE `t_permission` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `t_role`
--
DROP TABLE IF EXISTS `t_role`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `t_role` (
`role_id` bigint(20) NOT NULL AUTO_INCREMENT,
`role_name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `t_role`
--
LOCK TABLES `t_role` WRITE;
/*!40000 ALTER TABLE `t_role` DISABLE KEYS */;
INSERT INTO `t_role` VALUES (1,'管理员'),(2,'普通用户');
/*!40000 ALTER TABLE `t_role` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `t_role_permission`
--
DROP TABLE IF EXISTS `t_role_permission`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `t_role_permission` (
`roleid` bigint(20) DEFAULT NULL,
`permissionid` bigint(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `t_role_permission`
--
LOCK TABLES `t_role_permission` WRITE;
/*!40000 ALTER TABLE `t_role_permission` DISABLE KEYS */;
INSERT INTO `t_role_permission` VALUES (1,1);
/*!40000 ALTER TABLE `t_role_permission` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `t_user`
--
DROP TABLE IF EXISTS `t_user`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `t_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(100) DEFAULT NULL COMMENT '用户名',
`password` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `t_user`
--
LOCK TABLES `t_user` WRITE;
/*!40000 ALTER TABLE `t_user` DISABLE KEYS */;
INSERT INTO `t_user` VALUES (1,'test1','098f6bcd4621d373cade4e832627b4f6');
/*!40000 ALTER TABLE `t_user` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `t_user_role`
--
DROP TABLE IF EXISTS `t_user_role`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `t_user_role` (
`userid` bigint(20) DEFAULT NULL,
`roleid` bigint(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `t_user_role`
--
LOCK TABLES `t_user_role` WRITE;
/*!40000 ALTER TABLE `t_user_role` DISABLE KEYS */;
INSERT INTO `t_user_role` VALUES (1,1);
/*!40000 ALTER TABLE `t_user_role` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Dumping routines for database 'db_shiro'
--
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2024-07-19 16:19:08
- 配置内置的tomcat启动服务