二、Shiro安全框架-身份认证

二、Shiro安全框架-登陆实现

写在前面

上一章我们已经了解了Shiro的基本信息和架构,接下来我们用Shiro实现一个简单的登录。

1.登陆实现

1.1.搭建项目

创建一个名为”shiro-demo“的基于SpringBoot的web工程。

1.2.导入依赖

继承SpringBoot的父工程,导入web依赖和shiro整合spring的依赖以及其他相关依赖。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.5.RELEASE</version>
</parent>

<dependencies>
    <!-- 单元测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
        <version>4.12</version>
    </dependency>
    <!-- web依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- spring测试依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <!-- lombok依赖 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <!-- shiro整合spring依赖 -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.3.2</version>
    </dependency>
    <!-- spring-boot整合mybatis -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.1.1</version>
    </dependency>
    <!-- mysql驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.38</version>
    </dependency>
    <!-- 阿里巴巴数据库连接池 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.6</version>
    </dependency>
</dependencies>

1.3.主启动类

@SpringBootApplication
@MapperScan("cn.demo.shiro.mapper")
public class StartApplication {
    public static void main(String[] args) {
        SpringApplication.run(StartApplication.class);
    }
}

1.4.application.yml配置

配置端口和数据库相关

server:
  port: 8000

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql:///shiro
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

mybatis:
  mapper-locations: classpath:cn/demo/shiro/mapper/*Mapper.xml

1.5.web控制器

@RestController
@RequestMapping("/user")
public class UserController {

    /**
     * ----------------------------------------------------------
     * 登录方法
     * ----------------------------------------------------------
     **/
    @RequestMapping("/login")
    public String login(String username, String password){
        Subject subject = SecurityUtils.getSubject();
        try {
            if (!subject.isAuthenticated()){
                UsernamePasswordToken token = new UsernamePasswordToken(username, password);
                // 调用shiro的login方法
                subject.login(token);
            }
            return "用户名:"+username+"登陆成功!";
        } catch (UnknownAccountException uae ) {
            // 未知用户的帐号
            return "用户名错误!";
        } catch (IncorrectCredentialsException ice ) {
            // 密码不匹配
            return "密码错误!";
        } catch (LockedAccountException lae ) {
            // 该帐号被锁定了,无法登录。
            return "账号已被锁定!";
        } catch (AuthenticationException ae ) {
            return "身份认证失败!";
        }
    }
    
    /**
     * ----------------------------------------------------------
     * 获取用户列表方法,在shiro过滤配置中配置这个路径可以匿名访问
     * ----------------------------------------------------------
     **/
    @RequestMapping("/list")
    public String getList(){
        return "/user/list";
    }

    /**
     * ----------------------------------------------------------
     * 添加用户的方法,在shiro过滤配置中配置这个路径需要认证后可访问
     * ----------------------------------------------------------
     **/
    @RequestMapping("/add")
    public String addUser(){
        return "/user/add";
    }
    
    /**
     * ----------------------------------------------------------
     * 未登录,在前后端分离项目中返回json字符串给前端即可,单体项目可以
     * 重定向到登陆界面
     * ----------------------------------------------------------
     **/
    @RequestMapping("/unlogin")
    public String unlogin(){
        return "{'code':9999;msg:'未登录!'}";
    }
    
}

1.6.自定义Realm

自定义的Realm我们一般通过继承AuthorizingRealm类来做自己具体的实现。

public class MyRealm extends AuthorizingRealm {
    @Resource
    private UserMapper userMapper;

    /**
     * 返回当前领域的名称,必须唯一
     */
    @Override
    public String getName() {
        return "myRealm";
    }

    /**
     * 判断领域是否支持传入的token
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        // 表示当前realm只支持UsernamePasswordToken类型的token
        return token instanceof UsernamePasswordToken;
    }

    /**
     * 返回根据token拿到的认证信息
     * 一般我们只需要拿到账户信息即可
     */
    @Override
    public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String)token.getPrincipal();  //得到用户名
        User user = userMapper.selectByUsername(username);
        if(Objects.isNull(user)) {
            throw new UnknownAccountException("用户名错误,请重新输入用户名!"); //如果用户名错误
        }

        //如果身份认证验证成功,返回一个AuthenticationInfo实现;
        return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
    }

    /**
     * 返回用户的授权信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
}

1.7.Shiro配置

@Configuration
public class ShiroConfig {

    /**
     * ----------------------------------------------------------
     * 配置Reaml
     * ----------------------------------------------------------
     **/
    @Bean
    public MyRealm getMyRealm(){
        MyRealm myRealm = new MyRealm();
        myRealm.setCredentialsMatcher(new HashedCredentialsMatcher(Sha256Hash.ALGORITHM_NAME));
        return myRealm;
    }

    /**
     * ----------------------------------------------------------
     * 配置安全管理器
     * ----------------------------------------------------------
     **/
    @Bean
    public SecurityManager securityManager(Realm realm){
        return new DefaultWebSecurityManager(realm);
    }

    /**
     * ----------------------------------------------------------
     * 配置shiro过滤器
     *
     * 过滤类型:
     * 		anon:无需认证就可以访问
     * 		authc:必须认证了才能访问
     * 		user:必须用有了"记住我"功能才能用
     * 		perms:拥有对某个资源的权限才能访问
     * 		role:拥有某个角色权限才能访问
     * ----------------------------------------------------------
     **/
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        HashMap<String, String> filterChainDefinitionMap = new HashMap<>();

        filterChainDefinitionMap.put("/user/login", "anon");
        filterChainDefinitionMap.put("/user/list", "anon");
        filterChainDefinitionMap.put("/**", "authc");

        // 设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 配置shiro默认登录界面地址,前后端分离中登录界面跳转应由前端路由控制,后台仅返回json数据
        // 如果不进行配置那么默认为类路径下的login.jsp
        shiroFilterFactoryBean.setLoginUrl("/user/unlogin");
        // 设置登录成功后跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/user/index");
        // 设置未授权跳转的url
        shiroFilterFactoryBean.setUnauthorizedUrl("/user/unAuthorized");
        // 设置资源过滤集合
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * ----------------------------------------------------------
     * 配置加密方式,使用Shiro自带的Hash256进行加密
     * ----------------------------------------------------------
     **/
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);
        return  hashedCredentialsMatcher;
    }

}

2.模拟登录,访问资源

打开浏览器,在地址栏输入localhost:8000/user/list
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BUVIGF9Y-1638456704690)(C:UserslenovoAppDataRoamingTyporatypora-user-imagesimage-20211202212656231.png)]
在未登录的情况下成功访问到资源,因为我们在shiro的过滤配置中对这个资源设置的无需认证就可以访问

在地址栏输入localhost:8000/user/add,点击回车后显示未登录信息
在这里插入图片描述
因为我们在shiro的过滤配置中没有对/user/add这个资源进行单独配置访问权限,那么这个资源会被所配置的通配符配置进行拦截到,所以该资源就需要认证之后才能进行访问

在地址栏输入localhost:8000/user/login?username=root&password=root,点击回车后进行登录认证
在这里插入图片描述
这个时候我们可以看到已经显示登陆成功,这个时候再访问/user/add则可以成功访问
在这里插入图片描述到这里我们的简单认证登录实现就已经完成了

3.认证流程小结

SpringBoot整合Shrio的登录认证案例到这里就结束了,这篇文章没有对原理做过多的介绍,只是带大家进行一个shiro认证实现过程,这个案例的认证流程如下:
认证流程图

  • 用户在输入用户名密码提交登陆后会由Shiro来为我们进行认证,我们将用户输入的用户名和密码封装成Shiro认证所需要的UsernamePasswordToken对象,然后调用Sebject的login方法

  • Shrio使用DefualtSecurityManager调用ModularRealmAuthenticator领域认证器选择数据源

  • 我们自定义的Realm为登录认证提供安全数据(用户名、密码),通过用户名到数据库进行匹配

  • 使用凭证匹配器(通过我们配置的密码编码器)来进行密码匹配,如果匹配成功就跳转到登录页面,认证失败就返回错误信息

4.总结

本篇文章到这里就结束了,如果觉得对屏幕前的你有帮助的话就请来个一键三连吧,欢迎大家学习讨论,如果有不足之处请尽情指出,大家一起进步,学无止尽,Never Give Up!!!

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码

)">
< <上一篇
下一篇>>