shiro入门

shiro入门

1.Shiro的简介

公司项目中,常见的权限框架:shiro | spring security
Apache Shiro是一个功能强大且灵活的开源安全框架,可以清晰地处理身份验证,授权,企业会话管理和加密。

Apache Shiro 的首要目标是易于使用和理解。权限是很复杂的,甚至是痛苦的,但它没有必要这样。框架应该尽可能掩盖( 黑盒 | 空调)复杂的地方,露出一个干净而直观的 API(遥控器),来简化开发人员在使他们的应用程序安全上的努力。

Shiro能帮系统做什么:
1、做用户的身份认证(登录),判断用户是否系统用户(重点)
2、给系统用户授权,用来帮助系统实现不同的用户展示不同的功能(重点)
3、针对密码等敏感信息,进行加密处理(明文变成密文)(重点)
4、提供了Session管理,但是它的Session不是HttpSession,是它自己自带的
5、做授权信息的缓存管理,降低对数据库的授权访问
6、提供测试支持,因为它也是一个轻量级框架,它也可以直接针对代码进行使用Junit单元测试
7、提供Remeber me的功能,可以做用户无需再次登录即可访问某些页面
8、启用单点登录(SSO)功能。

2.Shiro提供的10大功能

shiro入门

  1. Authentication 身份认证/登录,验证用户是不是拥有相应的身份;

  2. Authorization 授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

  3. Session Management 会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;

  4. Cryptography 加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

  5. Web Support Web支持,可以非常容易的集成到Web环境;

  6. Caching 缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;

  7. Concurrency shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;

  8. Testing 提供测试支持;

  9. Run As :让我的用户暂时模拟其他用户权限登录使用;

  10. Remember Me 记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了

3.Shiro的架构图

接下来从Shiro内部来看下Shiro的架构,如下图所示:
shiro入门

**Subject主体:**可以看到主体可以是任何可以与应用交互的“用户”;

SecurityManager: 相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。

**Authenticator认证器:**负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证(登录)通过了;

Authrizer授权器: 或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;

Realm域(面试常问): 可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC实现,也可以是LDAP实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的Realm;

SessionManager: 如果写过Servlet就应该知道Session的概念,Session呢需要有人去管理它的生命周期,这个组件就是SessionManager;而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境、EJB等环境;所有呢,Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据;这样的话,比如我们在Web环境用,刚开始是一台Web服务器;
接着又上了台EJB服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可
以实现自己的分布式会话(如把数据放到Memcached服务器);

SessionDAO: DAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可以实现自己的SessionDAO,通过如JDBC写到数据库;比如想把Session放到Memcached中,可以实现自己的Memcached SessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;

CacheManager 缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能

Cryptography 密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。

AuthenticationStrategy 认证策略器: 两个及以上的自定义域 realm存在的时候,才使用这个! 我们这次上课不用!

4.Shiro框架的3个核心类

首先,从外部来看Shiro,即从应用程序角度的来观察如何使用Shiro完成工作。如下图:

shiro入门

Subject主体: 需要登录系统的东西,都是主体。 代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager,可以把Subject认为是一个门面;SecurityManager才是实际的执行者;

SecurityManager安全管理器: 即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与其他组件进行交互,相当于DispatcherServlet前端控制器;

Realm域: 一个用来做身份认证,以及授权的对象 Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,就是一个跟权限数据有关的数据源。

记住一点: Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。

shiro入门

5.基于角色权限ini配置文件的开发流程

  1. 创建maven项目, 在maven项目中, 引入 shiro-core 的核心包, junit 和common-loggin.

  2. 在resource文件夹下创建2个配置ini文件

    [users]
    #模拟从数据库查询的用户
    #数据格式 用户名=密码,角色1,角色2..
    mayun=123456,role1,role2
    mayuhang=654321,role3
    [roles]
    #模拟从数据库查询的角色和权限列表
    #数据格式 角色名=权限1,权限2
    role1=user:save,user:update
    role2=user:update,user.delete
    role3=user:find
    
  3. 在测试包下 test里面, 新建一个类(核心是BasicIniEnvironment剩下全是推理出来的),流程如下:

    1. 首先通过BasicIniEnvironment环境 ,去获取到我们的ini配置文件(去数据库判断账号密码是否正确, 同时 查询用户所有的角色 以及所有角色对应的权限)
    2. 通过这个环境对象创建SecurityManager对象
    3. 使用工具类SecurityUtils.set** 放入刚创建的SecurityManager对象
    4. SecurityUtils.getSubject 获取主体对象
    5. subject.login(UsernamePasswordToken …)进行登录认证 注意这个形参 需要用UsernamePasswordToken实现类
    6. subject.isAuthenticated()来判断该subject主体有没有登录认证成功
    7. subject.hasRole(String…)来判断是否有某个角色
    8. subject.isPermitted(String…)来判断有么有某个权限

6.基于realm自定义域ini配置文件的开发流程

1.新建配置文件shiro-realm.ini

后期要被替换掉!Springboot项目中, ShiroConfig.java类, 在这个类中 进行 放入自定义域 创建SecurityManager对象(后期配置类中返回的对象应该是DefaultSecurityManager, 把自定义域放入DefaultSecurityManager对象中 返回给springboot)!

[main]
#声明realm 自定义域YourRealm自定义的
permReam=com.woniuxy.utils.OurRealm
#注册realm到securityManager中
securityManager.realms=$permReam

以后在项目中需要配置这个配置方法 创建一个对象交给spring管理!

<bean class="org.apache.shiro...DefaultSecurityManager">
    //setter注入
	<property name="realms" ref="ourRealm上面配置好的  域的bean">
</bean>
@Bean
public SecurityManager doDefaultSecurityManager(OurRealm ourRealm){
    DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
    defaultSecurityManager.setRealms(ourRealm)
    retuen defaultSecurityManager;
}

2.开发我们的自定义域

  1. 首先要继承AuthorizingRealm。

  2. 重写2个方法 一个是 doGetAuthenticationInfo 认证 , doGetAuthorizationInfo 授权.

  3. 认证: doGetAuthenticationInfo ( 需要一个参数) 这个参数 就是 subject.login( 这个参数 );

这参数是 UsernamePasswordToken; 返回对象SimpleAuthenticationInfo(username,password ,myRealmName)

到这里 登录认证就结束。

  1. 授权: doGetAuthorizationInfo ( 需要一个参数) 这个参数 就是 UsernamePasswordToken中的"校长"; 在什么时候进入我的授权方法方法?? 答案是:需要判断有没有角色 subject.hasRole 以及 判断有没有这个权限, subject.isPermission()。

    授权返回值是SimpleAuthorizationInfo,这个对象中 有所有的角色和所有的权限!

  2. 认证和授权方法里面需要干嘛???? 认证中:需要去数据库查出信息 进行比对,成功就认证成功! 授权中: 通过用户名查出该用户下所有的角色, 和所有的权限!

登录和认证流程是分开的哈, 请看下图:

shiro入门

7.shiro入门程序 含代码

1.搭建基于ini文件的运行环境

		pom中导入shiro坐标:(plugins插件仅仅只是为了处理maven和idea兼容导致的启动问题)
<?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>com.woniuxy</groupId>
    <artifactId>shiro01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.7.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin> <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.7</version>
                <dependencies>
                    <dependency>
                        <groupId>org.apache.maven.shared</groupId>
                        <artifactId>maven-filtering</artifactId>
                        <version>1.3</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2.用户认证 BasicIniEnvironment

认证:身份认证/登录,验证用户是不是拥有相应的身份。基于shiro的认证,是通过subject的login方法完成用户认证工作的
(1)在resource目录下创建shiro的ini配置文件构造模拟数据(shiro-auth.ini)

[users]
#模拟从数据库查询的用户
#数据格式 用户名=密码
mayun=123456
mayuhang=654321

(2)测试用户认证

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.env.BasicIniEnvironment;
import org.apache.shiro.env.Environment;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Test;

/**
 * @author: mayuhang  <br/>
 * Date: 2021/4/29:0:32  <br/>
 * Description:
 */
public class TestLogin {
    @Test
    public void testLogin() throws Exception{
        //1.加载ini配置文件创建SecurityManager
        Environment environment = new BasicIniEnvironment("classpath:shiro-auth.ini");
        //2.获取securityManager
        SecurityManager securityManager = environment.getSecurityManager();
        //3.将securityManager绑定到当前运行环境
        SecurityUtils.setSecurityManager(securityManager);
        //4.创建主体(此时的主体还为经过认证)
        Subject subject = SecurityUtils.getSubject();
        /**
         * 模拟登录,和传统登陆了方式等不同的是需要使用主体进行登录
         */
        //5.构造主体登录的凭证(即用户名/密码)
        //第一个参数:登录用户名,第二个参数:登录密码
        UsernamePasswordToken token = new UsernamePasswordToken("mayun","123456");
        //6.主体登录
        subject.login(token);
        //7.验证是否登录成功
        System.out.println("用户登录成功, 认证状态:"+subject.isAuthenticated());
        //8.登录成功获取数据
        //getPrincipal 获取登录成功的安全数据
        System.out.println("从subject中获取用户信息:"+subject.getPrincipal());
    }
}

打印结果:

shiro入门

3.用户授权

授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限
(1)在resource目录下创建shiro的ini配置文件构造模拟数据(shiro-prem.ini)

[users]
#模拟从数据库查询的用户
#数据格式 用户名=密码,角色1,角色2..
mayun=123456,role1,role2
mayuhang=654321,role3
[roles]
#模拟从数据库查询的角色和权限列表
#数据格式 角色名=权限1,权限2
role1=user:save,user:update
role2=user:update,user.delete
role3=user:find

(2)完成用户授权

import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.config.IniSecurityManagerFactory;import org.apache.shiro.env.BasicIniEnvironment;import org.apache.shiro.env.Environment;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.subject.Subject;import org.apache.shiro.util.Factory;import org.junit.Test;/** * @author: mayuhang  <br/> * Date: 2021/4/29:22:44  <br/> * Description: */public class TestPerm {    @Test    public void testPerm() throws Exception {        //1.加载ini配置文件创建SecurityManager        Environment environment = new BasicIniEnvironment("classpath:shiro-auth.ini");        //2.获取securityManager        SecurityManager securityManager = environment.getSecurityManager();        //3.将securityManager绑定到当前运行环境        SecurityUtils.setSecurityManager(securityManager);        //4.创建主体(此时的主体还为经过认证)        Subject subject = SecurityUtils.getSubject();        /**         * 模拟登录,和传统等不同的是需要使用主体进行登录         */        //5.构造主体登录的凭证(即用户名/密码)        //第一个参数:登录用户名,第二个参数:登录密码        UsernamePasswordToken token = new UsernamePasswordToken("mayuhang", "654321");        //6.主体登录        subject.login(token);        //7.用户认证成功之后才可以完成授权工作        boolean hasPerm = subject.isPermitted("user:find");        System.out.println("从subject中获取用户信息:"+subject.getPrincipal());        System.out.println("用户是否具有find权限=" + hasPerm);    }}

subject.login(UsernamePasswordToken … token) //执行shiro内置的登录认证

subject.isAuthenticated() //是否认证(登录)成功(成功就是true)

subject.getPrincipal() //获取登录的用户名

subject.hasRole(String … perm) //是否这个角色

subject.isPermitted(String …perm) //是否又这个权限

4.自定义域(重点 面试) 只要记住 继承AuthorizingRealm

Realm域:Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

(1)自定义域(Realm)

package com.woniuxy.shiro;import org.apache.shiro.authc.*;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 java.util.ArrayList;import java.util.List;/** * @author: mayuhang  <br/> * Date: 2021/4/29:23:14  <br/> * Description: 自定义域 *              继承AuthorizingRealm *             重写  AuthorizationInfo   Z 授权 *                  AuthenticationInfo  C 认证 */public class MyRealm extends AuthorizingRealm {    @Override    public void setName(String name){        super.setName("myRealm");    }    /**     * 授权:授权的主要目的就是查询 数据库获取当前用户的所有角色和权限信息     *     *    在前后端不分离的情况下,     *    在html页面使用<shiro>|后端使用shiro的@checkRole,@checkPermission相关注解才能触发     *    其实前后端分离项目, 这个授权意义就不大了, 不通过controller加注解来控制权限     */    @Override    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {        // 1.从principals获取已认证用户的信息 类比之前打印的方法 "从subject中获取用户信息:"+subject.getPrincipal();        String username = (String) principals.getPrimaryPrincipal();         /**         * 正式系统:应该从数据库中根据用户名或者id查询 所有权限         * 这里为了演示,手动构造         */        // 2.模拟从数据库中查询的用户所有权限        List<String> permissions = new ArrayList<String>();        permissions.add("user:save");// 用户的创建        permissions.add("user:update");// 商品添加权限        // 3.模拟从数据库中查询的用户所有角色        List<String> roles = new ArrayList<String>();        roles.add("role1");        roles.add("role2");        // 4.构造权限数据(就是个实体类^ ^)        SimpleAuthorizationInfo simpleAuthorizationInfo = new                SimpleAuthorizationInfo();        // 5.通过当前查询的权限数据保存到simpleAuthorizationInfo        simpleAuthorizationInfo.addStringPermissions(permissions);        // 6.通过当前用户,查询的角色数据保存到simpleAuthorizationInfo        simpleAuthorizationInfo.addRoles(roles);        return simpleAuthorizationInfo;    }    /**     * 身份认证方法:认证的主要目的,比较用户输入的用户名密码是否和数据库中的一致     *     * 需要在用户登录系统时触发     * 该方法将是我们主要的方法, 流程是登录后, 携带token 与权限信息 发送到 vue中 存储浏览器     */    @Override    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {        //1.一个简单的用户名/密码身份验证令牌,以支持使用最广泛的身份验证机制        UsernamePasswordToken upToken = (UsernamePasswordToken)token;        //2.获取输入的用户名密码        String username = upToken.getUsername();        String password = new String(upToken.getPassword());        /**         * 3.验证用户名密码是否正确         * 正式系统:应该从数据库中通过用户查询密码(一般会加密), 比较密码是否一致         * 此处测试,只要输入的密码为123456则登录成功         */        if(!password.equals("123456")) {            throw new RuntimeException("用户名或密码错误");//抛出异常表示认证失败        }else {            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,password,this.getName());            return info;        }    }}

(2)配置shiro的ini配置文件(shiro-realm.ini)

[main]#声明realmpermReam=com.woniuxy.shiro.MyRealm#注册realm到securityManager中securityManager.realms=$permReam

(3)测试验证流程

import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.env.BasicIniEnvironment;import org.apache.shiro.env.Environment;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.subject.Subject;import org.junit.Test;/** * @author: mayuhang  <br/> * Date: 2021/4/29:23:43  <br/> * Description: */public class TestRealm {    @Test    public void testPerm() throws Exception {        //1.加载ini配置文件创建SecurityManager        Environment environment = new BasicIniEnvironment("classpath:shiro-realm.ini");        //2.获取securityManager        SecurityManager securityManager = environment.getSecurityManager();        //3.将securityManager绑定到当前运行环境        SecurityUtils.setSecurityManager(securityManager);        //4.创建主体(此时的主体还为经过认证)        Subject subject = SecurityUtils.getSubject();        /**         * 模拟登录,和传统等不同的是需要使用主体进行登录         */        //5.构造主体登录的凭证(即用户名/密码)        //第一个参数:登录用户名,第二个参数:登录密码        UsernamePasswordToken upToken = new UsernamePasswordToken("mayuhang", "123456");        //6.主体登录        subject.login(upToken);        //7.用户认证成功之后才可以完成授权工作      //认证 是否登录成功        System.out.println("判断"+subject.getPrincipal()+"是否认证成功"+subject.isAuthenticated());        //是否有这个角色 或者 权限        System.out.println("判断是否有role1的角色"+subject.hasRole("role1"));        System.out.println("判断是否有user:insert的权限"+subject.isPermitted("user:insert"));        System.out.println("判断是否有user:add的权限"+subject.isPermitted("user:add"));    }}

我们通过自定义realm实现了认证! 不再通过 ini文件写死账号密码来认证! 自定义域 也就是以后我们在项目开发过程中, 用的方式!

课上代码:

package com.woniuxy.shiro;import org.apache.shiro.authc.*;import org.apache.shiro.authz.AuthorizationInfo;import org.apache.shiro.authz.SimpleAuthorizationInfo;import org.apache.shiro.crypto.hash.SimpleHash;import org.apache.shiro.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;import java.util.HashSet;import java.util.Set;/** * @author: mayuhang  <br/> * Date: 2021/4/30:15:21  <br/> * Description:  自定义域  这个是给  单一用户 进行 认证和授权的! *          认证的数据放什么实体中:            SimpleAuthenticationInfo *          授权的数据(角色和权限)放什么实体中:  SimpleAuthorizationInfo *          这个两个对象去了哪里呢?? *          SecurityManager  它管理了我们单一用户的认证授权信息  以及 所有的 subject */public class MyRealm extends AuthorizingRealm{    @Override    public String getName() {        return "MyRealm";    }    //授权  授予登录的这个角色和权限!    @Override    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {        //通过形参 获取用户名        String username = (String) principals.getPrimaryPrincipal();        /*        *通过用户名 去数据库查询 所有的角色  和   权限        *        *先去数据库查询当前用户 所有的角色        *        *再去通过 所有的角色  查询出所有的权限        *        */        Set<String> roles = new HashSet();        roles.add("role1");        roles.add("role2");        Set<String> perms = new HashSet();        perms.add("user:add");        perms.add("user:delete");        perms.add("user:update");        perms.add("user:find");        perms.add("user:find");        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();        simpleAuthorizationInfo.addRoles(roles);        simpleAuthorizationInfo.addStringPermissions(perms);        return simpleAuthorizationInfo;    }    //认证  确定你的用户名 密码正确    @Override    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {        //自定义实现 登录验证        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;        //我在自定义认证方法中,可以通过 入参 拿到我的账号和密码        String username = usernamePasswordToken.getUsername();        String password =new String(usernamePasswordToken.getPassword());        //1.用账号去数据库查询出你的密码!  密码一般加密的        //2.把当前变量的 password用同样的方式 加密, 然后 和数据库查出的密码进行比对!        //3.比对两个密码!  我们一般使用的是非对称加密!(找回密码!忘记密码! 一般验证账号正确, 重置密码!)        if(password.equals("123456")){            return new SimpleAuthenticationInfo(username,password,getName());        }        return null;    }}
上一篇:acwingC++语法基础课第四课判断语句的习题练习游戏时间 加薪 动物657. 选择练习1 671. DDD


下一篇:C/C++编程笔记:访问修饰符丨Public、Private和Protected