SpringBoot整合Shiro完成认证

三、SpringBoot整合Shiro思路

image-20200812095527218

首先从客户端发来的所有请求都经过Shiro过滤器,如果用户没有认证的都打回去进行认证,认证成功的,再判断是否具有访问某类资源(公有资源,私有资源)的权限,如果没有权限,访问失败;如果有权限访问成功。注意:客户端传来的token要和realm中的认证信息进行相同规则的比较(加密算法要一致)。

shiro常见的过滤器

Filter NameClass功能
anonorg.apache.shiro.web.filter.authc.AnonymousFilter指定url可以匿名访问
authcorg.apache.shiro.web.filter.authc.FormAuthenticationFilter指定url需要form表单登录,默认会请求username、password。rememberMe等参数并尝试登录,如果登录不了就会跳转到loginUrl配置的路径。我们也可以用这个过滤器设置默认的路径。
authcBasicorg.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter指定url需要http认证
authcBearerorg.apache.shiro.web.filter.authc.BearerHttpAuthenticationFilter需要用户认证成功才能访资源否则就通过http请求登录
logoutorg.apache.shiro.web.filter.authc.LogoutFilter登出过滤器,配置URL实现退出登录的功能。
noSessionCreationorg.apache.shiro.web.filter.session.NoSessionCreationFilter禁止创建会话
permsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter指定权限才能访问
portorg.apache.shiro.web.filter.authz.PortFilter指定端口才能访问
restorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilter将HTPP请求方法转化成权限字符串
rolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFilter指定角色才能访问
sslorg.apache.shiro.web.filter.authz.SslFilter需要https请求才能访问
userorg.apache.shiro.web.filter.authc.UserFilter需要已登录或者记住我的用户才能访问

SpringBoot整合Shiro实现认证

没有加密版

1、首先创建SpringBoot应用,勾选web模块

2、然后导入SpringBoot与shiro整合的依赖

<dependencies>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
    </dependency>
    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--引入SpringBoot与shiro整合的依赖-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring-boot-starter</artifactId>
        <version>1.5.3</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

3、创建Shiro的配置类。

@Configuration
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
        // 创建ShiroFilterFactoryBean
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        filterFactoryBean.setSecurityManager(defaultWebSecurityManager);

        //配置受限资源,index是受限资源,authc
        Map<String, String> map = new HashMap<String, String>();
        // /**代表匹配所有url
        map.put("/**", "authc");
        // /user/login 是可以匿名访问的也就是公有资源
        map.put("/user/login", "anon");
        filterFactoryBean.setFilterChainDefinitionMap(map);

        // 设置默认认证路径 其实shiro默认的认证路径就是login.jsp
        filterFactoryBean.setLoginUrl("/login.jsp");

        return filterFactoryBean;

    }

    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }

    @Bean
    public Realm getRealm() {
        CustomRelam customRelam = new CustomRelam();
        return customRelam;
    }

}

4、创建自定义realm。

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.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

public class CustomRelam extends AuthorizingRealm {
    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String principal = (String) authenticationToken.getPrincipal();
        // 实际开发中 先确认传来的token用户名是否存在,如果存在,然后根据有户名查询密码返回,进而判断密码是否正确
        
        // 这里假设从数据库中查到的用户名是 xiaozhang
        String username = "xiaozhang";
        if (username.equals(principal)) {
            // 密码还没加密,下一步会加盐并加密
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal,
                    "123", this.getName());
            // 用户名和面都判断完成后可以返回
            return simpleAuthenticationInfo;
        }

        return null;
    }
}

5、编写controller,处理登录和退出的请求

package club.qy.datao.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author 胡
 */
@Controller
@RequestMapping("/user")
public class UserController {
	// 退出操作
    @RequestMapping("/logout")
    public String logout() {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/login.jsp";
    }
	// 登录请求
    @PostMapping("/login")
    public String login(String username, String password) {
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken passwordToken = new UsernamePasswordToken(username, password);
        try {
            subject.login(passwordToken);
            return "redirect:/index.jsp";
        } catch (UnknownAccountException e) {
            System.out.println("用户名错误" + e.getMessage());

        } catch (IncorrectCredentialsException e) {
            System.out.println("密码错误" + e.getMessage());
        }

        return "redirect:/login.jsp";

    }

}

下面开始连接数据库MySQLMD5+salt验证登录的认证

数据库表的创建

DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(40) DEFAULT NULL,
  `password` varchar(40) DEFAULT NULL,
  `salt` varchar(40) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;

注册页面

<%@page contentType="text/html; UTF-8" pageEncoding="UTF-8" %>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
        <h1 class="">注册页</h1>
        <form action="${pageContext.request.contextPath}/user/register" method="post">
            用户名:<input type="text" name="username"><br/>
            密码: <input type="password" name="password"><br/>
             <input type="submit" value="注册">
        </form>

</body>
</html>

导入依赖

<dependencies>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
    </dependency>
    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--引入SpringBoot与shiro整合的依赖-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring-boot-starter</artifactId>
        <version>1.5.3</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.8</version>
    </dependency>
    <!--mybatis-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.2</version>
    </dependency>
    <!--druid-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.19</version>
    </dependency>


    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

配置文件

server.port=8081
server.servlet.context-path=/shiro
spring.application.name=shiro

spring.mvc.view.suffix=.jsp
spring.mvc.view.prefix=/

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql://localhost:3309/shiro?characterEncoding=UTF-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456

mybatis.type-aliases-package=club.qy.datao.entity
mybatis.mapper-locations=classpath:mapper/*.xml

创建实体类

@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private String username;
    private String password;
    private Integer id;
    private String salt;

}

创建UserDao

@Repository
@Mapper
public interface UserDao {
    void saveUser(User user);
}

UserDaoMapper.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="club.qy.datao.dao.UserDao">
    <insert id="saveUser" parameterType="club.qy.datao.entity.User" useGeneratedKeys="true" keyProperty="id">
        insert into t_user values (#{id},#{username},#{password},#{salt})
    </insert>
</mapper>

service实现

@Service
@Transactional
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    @Override
    public void register(User user) {

        //1。生成随机盐
        String saltString = SaltUtil.getSaltString(9);
        //2、给用户设置盐
        user.setSalt(saltString);
        //3、明文密码进行散列算法1024次,并加盐
        Md5Hash md5Hash = new Md5Hash(user.getPassword(), saltString, 1024);
        //4、将转成16进制的密码封装到user对象中
        user.setPassword(md5Hash.toHex());
        //5、插入数据库
        userDao.saveUser(user);

    }
}

SaltUtil工具类

public class SaltUtil {

    public static String getSaltString(int n){

        StringBuilder stringBuilder = new StringBuilder();
        char[] array = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!@~><?abcdefghijklmnopqrstuvwxyz".toCharArray();
        for (int i = 0; i < n; i++) {
            char ch = array[new Random().nextInt(array.length)];
            stringBuilder.append(ch);
        }
        return stringBuilder.toString();
    }

}

ShiroConfig类

@Configuration
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
        // 创建ShiroFilterFactoryBean
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        filterFactoryBean.setSecurityManager(defaultWebSecurityManager);

        //配置受限资源,index是受限资源,authc
        Map<String, String> map = new HashMap<String, String>();
        // /**代表匹配所有url
        map.put("/**", "authc");
        // /user/login 是可以匿名访问的也就是公有资源
        map.put("/user/**", "anon");
        map.put("/register.jsp", "anon");
        filterFactoryBean.setFilterChainDefinitionMap(map);

        // 设置默认认证路径 其实shiro默认的认证路径就是login.jsp
        filterFactoryBean.setLoginUrl("/login.jsp");
        return filterFactoryBean;
    }
     @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }

    @Bean
    public Realm getRealm() {
        CustomRelam customRelam = new CustomRelam();
        return customRelam;
    }
}

image-20200813153209038

原文地址:https://www.cnblogs.com/dataoblogs/p/14121883.html