一文总结 Shiro 实战教程
Posted Java Fans
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文总结 Shiro 实战教程相关的知识,希望对你有一定的参考价值。
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。
🍎个人主页:Java Fans的博客
🍊个人信条:不迁怒,不贰过。小知识,大智慧。
💞当前专栏:SpringBoot 框架从入门到精通
✨特色专栏:国学周更-心性养成之路
🥭本文内容:一文总结 Shiro 实战教程
文章目录
1.权限的管理
1.1 什么是权限管理
基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制
,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。
权限管理包括用户身份认证
和授权
两部分,简称认证授权
。对于需要访问控制的资源用户首先经过身份认证,认证通过后用户具有该资源的访问权限方可访问。
1.2 什么是身份认证
身份认证
,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。对于采用指纹等系统,则出示指纹;对于硬件Key等刷卡系统,则需要刷卡。
1.3 什么是授权
授权,即访问控制
,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的
2.什么是shiro
Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.
Shiro 是一个功能强大且易于使用的Java安全框架,它执行身份验证、授权、加密和会话管理。使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序—从最小的移动应用程序到最大的web和企业应用程序。
Shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架。
3.shiro的核心架构
3.1 Subject
Subject即主体
,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。 Subject在shiro中是一个接口,接口中定义了很多认证授权相关的方法,外部程序通过subject进行认证授权,而subject是通过SecurityManager安全管理器进行认证授权
3.2 SecurityManager
SecurityManager即安全管理器
,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。
SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。
3.3 Authenticator
Authenticator即认证器
,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。
3.4 Authorizer
Authorizer即授权器
,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。
3.5 Realm
Realm即领域
,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。
- 注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。
3.6 SessionManager
sessionManager即会话管理
,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。
3.7 SessionDAO
SessionDAO即会话dao
,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。
3.8 CacheManager
CacheManager即缓存管理
,将用户权限数据存储在缓存,这样可以提高性能。
3.9 Cryptography
Cryptography即密码管理
,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。
4. shiro中的认证
4.1 认证
身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。
4.2 shiro中认证的关键对象
- Subject:主体
访问系统的用户,主体可以是用户、程序等,进行认证的都称为主体;
- Principal:身份信息
是主体(subject)进行身份认证的标识,标识必须具有唯一性
,如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal)。
- credential:凭证信息
是只有主体自己知道的安全信息,如密码、证书等。
4.3 认证流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cEb7L6Z6-1680591385485)(Shiro 实战教程.assets/image-20200521204452288.png)]
4.4 认证的开发
1. 创建项目并引入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.3</version>
</dependency>
2. 引入shiro配置文件并加入如下配置
[users]
mosin=1234
tom=1234
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2zU9GESA-1680591385485)(Shiro 实战教程.assets/image-20220528172213825.png)]
3.开发认证代码
/**
* @author: mosin
* @version: v1.0
*/
public class ShiroTest
public static void main(String[] args)
//创建默认的安全管理器
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//创建安全管理器需要的realm对象
IniRealm iniRealm = new IniRealm("classpath:realm.ini");
//安全管理器设置realm对象
defaultSecurityManager.setRealm(iniRealm);
//将安全管理器注入安全工具类 用于获取认证的主体
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取认证的主体
Subject subject = SecurityUtils.getSubject();
//创建令牌
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("mosin", "1234");
try
//认证 通过没有任何的异常
subject.login(usernamePasswordToken);
//验证是否通过
boolean authenticated = subject.isAuthenticated();
System.out.println("认证通过:"+authenticated);
catch (UnknownAccountException e)
e.printStackTrace();
System.out.println("用户名错误!");
catch (IncorrectCredentialsException e)
e.printStackTrace();
System.out.println("密码错误!");
-
DisabledAccountException(帐号被禁用)
-
LockedAccountException(帐号被锁定)
-
ExcessiveAttemptsException(登录失败次数过多)
-
ExpiredCredentialsException(凭证过期)等
4.5 自定义Realm
上边的程序使用的是Shiro自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm。
1.shiro提供的Realm
2.根据认证源码认证使用的是SimpleAccountRealm
SimpleAccountRealm的部分源码中有两个方法一个是 认证 一个是 授权
,
public class SimpleAccountRealm extends AuthorizingRealm
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
SimpleAccount account = getUser(upToken.getUsername());
if (account != null)
if (account.isLocked())
throw new LockedAccountException("Account [" + account + "] is locked.");
if (account.isCredentialsExpired())
String msg = "The credentials for account [" + account + "] are expired";
throw new ExpiredCredentialsException(msg);
return account;
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
String username = getUsername(principals);
USERS_LOCK.readLock().lock();
try
return this.users.get(username);
finally
USERS_LOCK.readLock().unlock();
3.自定义realm
/**
* 自定义realm
*/
public class CustomerRealm extends AuthorizingRealm
//认证方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
return null;
//授权方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
String principal = (String) token.getPrincipal();
if("mosin".equals(principal))
return new SimpleAuthenticationInfo(principal,"123",this.getName());
return null;
4.使用自定义Realm认证
public class TestAuthenticatorCustomerRealm
public static void main(String[] args)
//创建securityManager
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//IniRealm realm = new IniRealm("classpath:realm.ini");
//设置为自定义realm获取认证数据
defaultSecurityManager.setRealm(new CustomerRealm());
//将安装工具类中设置默认安全管理器
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取主体对象
Subject subject = SecurityUtils.getSubject();
//创建token令牌
UsernamePasswordToken token = new UsernamePasswordToken("mosin", "1234");
try
subject.login(token);//用户登录
System.out.println("登录成功");
catch (UnknownAccountException e)
e.printStackTrace();
System.out.println("用户名错误!!");
catch (IncorrectCredentialsException e)
e.printStackTrace();
System.out.println("密码错误!!!");
4.6 使用MD5和Salt
实际应用是将盐和散列后的值存在数据库中,自动realm从数据库取出盐和加密后的值由shiro完成密码校验。
1.自定义md5+salt的realm
/**
* 自定义md5+salt realm
*/
public class CustomerMD5Realm extends AuthorizingRealm
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
return null;
//认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
String principal = (String) token.getPrincipal();
//根据用户名查询数据库
if("mosin".equals(principal))
// 参数1:用户名 参数2:密码 参数3:盐 参数4:自定义realm的名字
System.out.println(this.getName());
return new SimpleAuthenticationInfo(principal, "800d63a19662b2ba95bc2ffa01ab4804", ByteSource.Util.bytes("mosin"),this.getName());
return null;
2.使用md5 + salt 认证
public class CustomerMD5RealmTest
public static void main(String[] args)
//创建安全管理器
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//创建自定义MD5Realm对象
CustomerMD5Realm customerMD5Realm = new CustomerMD5Realm();
//创建密码认证匹配器对象
HashedCredentialsMatcher md5 = new HashedCredentialsMatcher("MD5");
//设置散列的次数
md5.setHashIterations(1024);
//设置密码认证匹配器对象
customerMD5Realm.setCredentialsMatcher(md5);
//设置安全管理器的 认证安全数据源
defaultSecurityManager.setRealm(customerMD5Realm);
//设置安全工具类的安全管理器
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取认证的主体
Subject subject = SecurityUtils.getSubject();
//创建令牌
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("mosi", "12345");
//登录认证
try
subject.login(usernamePasswordToken);
System.out.println("认证通过:"+subject.isAuthenticated());
catch (UnknownAccountException e)
e.printStackTrace();
System.out.println("用户名错误");
catch (IncorrectCredentialsException e)
e.printStackTrace();
System.out.println("密码错误!!!");
5. shiro中的授权
5.1 授权
授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。
5.2 关键对象
授权可简单理解为who对what(which)进行How操作:
Who,即主体(Subject)
,主体需要访问系统中的资源。
What,即资源(Resource)
,如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型
和资源实例
,比如商品信息为资源类型
,类型为t01的商品为资源实例
,编号为001的商品信息也属于资源实例。
How,权限/许可(Permission)
,规定了主体对资源的操作许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为001用户的修改权限等,通过权限可知主体对哪些资源都有哪些操作许可。
5.3 授权流程
5.4 授权方式
-
基于角色的访问控制
-
RBAC基于角色的访问控制(Role-Based Access Control)是以角色为中心进行访问控制
if(subject.hasRole("admin")) //操作什么资源
-
-
基于资源的访问控制
-
RBAC基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制
if(subject.isPermission("user:update:01")) //资源实例 //对01用户进行修改 if(subject.isPermission("user:update:*")) //资源类型 //对01用户进行修改
-
5.5 权限字符串
权限字符串的规则是:资源标识符:操作:资源实例标识符,意思是对哪个资源的哪个实例具有什么操作,“:”是资源/操作/实例的分割符,权限字符串也可以使用*通配符。
例子:
- 用户创建权限:user:create,或user:create:*
- 用户修改实例001的权限:user:update:001
- 用户实例001的所有权限:user:*:001
5.6 shiro中授权编程实现方式
-
编程式
Subject subject = SecurityUtils.getSubject(); if(subject.hasRole(“admin”)) //有权限 else //无权限
-
注解式
@RequiresRoles("admin") public void hello() //有权限
-
标签式
JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成: <shiro:hasRole name="admin"> <!— 有权限—> </shiro:hasRole> 注意: Thymeleaf 中使用shiro需要额外集成!
5.7 开发授权
1.realm的实现
public class CustomerRealm extends AuthorizingRealm
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
String primaryPrincipal = (String) principals.getPrimaryPrincipal();
System.out.println("primaryPrincipal = " + primaryPrincipal);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRole("admin");
simpleAuthorizationInfo.addStringPermission("user:update:*");
simpleAuthorizationInfo.addStringPermission("product:*:*");
return simpleAuthorizationInfo;
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
String principal = (String) token.getPrincipal();
if("xiaochen".equals(principal))
String password = "3c88b338102c1a343bcb88cd3878758e";
String salt = "Q4F%";
return new SimpleAuthenticationInfo(principal,password,
ByteSource.Util.bytes(salt),this.getName());
return null;
2.授权
public class CustomerMD5RealmTest
public static void main(String[] args)
//创建安全管理器
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//创建自定义MD5Realm对象
CustomerMD5Realm customerMD5Realm = new CustomerMD5Realm();
//创建密码认证匹配器对象
HashedCredentialsMatcher md5 = new HashedCredentialsMatcher("md5");
//设置加密的次数
md5.setHashIterations(1024);
//设置密码认证匹配器对象
customerMD5Realm.setCredentialsMatcher(md5);
//设置安全管理器的 认证安全数据源
defaultSecurityManager.setRealm(customerMD5Realm);
//设置安全工具类的安全管理器
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取认证的主体
Subject subject = SecurityUtils.getSubject();
//创建令牌
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("mosin", "12345");
//登录认证
try
subject.login(usernamePasswordToken);
System.out.println("认证通过:"+subject.isAuthenticated());
Java相关框架资料及其基础资料、进阶资料、测试资料之分享
通过该篇文章获取资料,包含视频等相关资料
张开涛先生的github系列地址如下:
https://github.com/zhangkaitao/shiro-example
张开涛先生的shiro博客文章系列地址如下:
http://jinnianshilongnian.iteye.com/blog/2049092
上述列出的,可以作为朋友们的学习参考,当然技术每时每刻不在更新,但是底层原理却是不变的。
关于shiro和Java流行框架(Spring+SpringMVC+MyBatis或SpringBoot等案例)
大家可以去github上找,或者直接去码云上借鉴。
码云上的案例都还不错,感谢开源并乐于分享的程序爱好者们。
至于博客文章,几年前的和现在的shiro相关案例,大家都可以参考借鉴。
为了不做拿来主义,我觉得有必要分享分享,即便相关的案例比较多,但是每篇博文我想都从不同的角度看待shiro。
俗话说:对于哈姆莱特,一千个读者有一千个体会。
至于原话是否如此,我也懒得百度搜索了,总而言之每个编程爱好者们对于技术,都有自己的视角。
一、导入依赖
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.2</version>
</dependency>
二、自定义Realm
package com.shiro;
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.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import com.dao.UserDao;
import com.entity.UserEntity;
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserDao userDao;
/**
* 密码匹配凭证管理器
*
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 采用MD5方式加密
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
// 设置加密次数
hashedCredentialsMatcher.setHashIterations(1024);
return hashedCredentialsMatcher;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.addStringPermission("sys");
System.out.println("开始授权");
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken=(UsernamePasswordToken) token;
String username=upToken.getUsername();
String password=new String(upToken.getPassword());
UserEntity user=new UserEntity();
user.setLoginName(username);
user=userDao.selectOne(user);
System.out.println("===========");
if(user!=null){
if(user.getPassword().equals(password)){
return new SimpleAuthenticationInfo(username,password,getName());
}
}
throw new UnauthenticatedException();
}
}
三、spring-shiro.xml配置文件内容
<?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:util="http://www.springframework.org/schema/util"
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.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- 自定义Realm -->
<bean id="myRealm" class="com.shiro.MyRealm"/>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"/>
</bean>
<!-- Shiro过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager"/>
<!-- 身份认证失败,则跳转到登录页面的配置 -->
<property name="loginUrl" value="/login.html"/>
<!-- 权限认证失败,则跳转到指定页面 -->
<property name="unauthorizedUrl" value="/login.html"/>
<!-- Shiro连接约束配置,即过滤链的定义 -->
<property name="filterChainDefinitions">
<value>
/login.html=anon
/index.html=anon
/**=authc
</value>
</property>
</bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 开启Shiro注解 -->
<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>
</beans>
四、web.xml内容
<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>
五、测试相关的实体类及其DAO、Service等
UserEntity.java
package com.entity;
import java.io.Serializable;
import com.baomidou.mybatisplus.activerecord.Model;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableName;
@TableName("user")
public class UserEntity extends Model<UserEntity> {
/**
*
*/
private static final long serialVersionUID = 1L;
private Integer id;
@TableField("login_name")
private String loginName;
private String password;
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 String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
protected Serializable pkVal() {
// TODO Auto-generated method stub
return id;
}
}
UserDao.java
package com.dao;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.entity.UserEntity;
public interface UserDao extends BaseMapper<UserEntity>{
}
UserService.java
package com.service;
import com.baomidou.mybatisplus.service.IService;
import com.entity.UserEntity;
public interface UserService extends IService<UserEntity>{
}
UserServiceImpl.java
package com.service.impl;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.dao.UserDao;
import com.entity.UserEntity;
import com.service.UserService;
@Service
public class UserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements UserService {
}
UserDao.xml
<?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">
<mapper namespace="com.dao.UserDao">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.entity.UserEntity">
<id column="id" property="id" />
<result column="login_name" property="loginName" />
<result column="password" property="password" />
</resultMap>
</mapper>
最后,我想说的是,大家尽可能学习参照官网,毕竟官网是比较权威比较全面的。
当然,对于完全不懂不知道的,可以通过视频或者文档及入门程序达到有使用并了解的程度,然后在这个基础上多深入。当然,任何一门技术学习和使用过程中,问题总会不断的。
没关系,问题多,虽然挺操蛋的,但是越是觉得难受不爽,我想这就是上升带来的阻力和痛苦吧。就好比修仙者们,修仙的过程是痛苦的,当达到一定的程度时,就会天外飞仙,直达天堂。
哈哈,说过了。
总而言之,希望个人的小小分享,能给大家带来帮助。
以上是关于一文总结 Shiro 实战教程的主要内容,如果未能解决你的问题,请参考以下文章
『Python开发实战菜鸟教程』实战篇:一文带你了解人脸识别应用原理及手把手教学实现自己的人脸识别项目
『Python开发实战菜鸟教程』实战篇:一文带你了解人脸识别应用原理及手把手教学实现自己的人脸识别项目
『Python开发实战菜鸟教程』实战篇:一文带你了解人脸识别应用原理及手把手教学实现自己的人脸识别项目