Spring Boot Shiro 用户认证

参考源码

Spring-Boot-Shiro

1,添加引用

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         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.springboot</groupId>
    <artifactId>Spring-Boot-Shiro</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>demo</name>
    <description>Demo project for Spring Boot</description>

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

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.11</version>
        </dependency>
        <!--数据库-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.12</version>
        </dependency>
        <!-- shiro-spring -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

引用shiro的jar包

<dependency>
     <groupId>org.apache.shiro</groupId>
     <artifactId>shiro-spring</artifactId>
     <version>1.4.0</version>
</dependency>

2,application.yml

server:
  port: 8080
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://192.168.16.128:3306/ds?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=UTC
mybatis:
  mapper-locations: classpath:mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true

3,ShiroConfig

添加shiro配置。

ShiroRealm 实现用户的登录,用户权限查询。 有2个待实现的方法:

AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)  :用户登录
AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal)  : 查询用户角色,权限

ShiroFilterFactoryBean 设置权限集合,登录路径,登录成功路径等。

anon	匿名拦截器,即不需要登录即可访问;一般用于静态资源过滤
authc	需登录,如果没有登录会跳到相应的登录页面登录
perms	权限授权拦截器,验证用户是否拥有所有权限;示例/user/**=perms["user:create"]
roles	角色授权拦截器,验证用户是否拥有所有角色;示例/admin/**=roles[admin]
ssl		SSL拦截器,只有请求协议是https才能通过;否则自动跳转会https端口443;其他和port拦截器一样;
user	登录/记住我登录的都可;示例/**=user

SecurityManager 安全管理,是上面的配置生效。


@Configuration
public class ShiroConfig {
    
    

    /**
     * 配置权限
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
    
    
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setSuccessUrl("/index");
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/fonts/**", "anon");
        filterChainDefinitionMap.put("/img/**", "anon");
        filterChainDefinitionMap.put("/druid/**", "anon");
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/", "anon");
        //如果要使用 Remember : 需把 authc 改成 user
        //user指的是用户认证通过或者配置了Remember Me记住用户登录状态后可访问
        filterChainDefinitionMap.put("/**", "authc");


        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

    /**
     * 使配置生效
     */
    @Bean
    public SecurityManager securityManager() {
    
    
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(shiroRealm());
        return securityManager;
    }

    /**
     * 配置授权,登录的Realm
     */
    @Bean
    public ShiroRealm shiroRealm() {
    
    
        ShiroRealm shiroRealm = new ShiroRealm();
        return shiroRealm;
    }

}

4,ShiroRealm

ShiroRealm 这节只实现 用户登录,用户权限查询下一节实现


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

    /**
     * 获取用户角色和权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
    
    
    	//下一章讲解
		return null ;
    }

    /**
     * 登录认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
    
        String userName = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());

        System.out.println("用户" + userName + "认证-----ShiroRealm.doGetAuthenticationInfo");
        User user = userMapper.getUserByName(userName);

        if (user == null) {
    
    
            throw new UnknownAccountException("用户名或密码错误!");
        }
        if (!password.equals(user.getPassword())) {
    
    
            throw new IncorrectCredentialsException("用户名或密码错误!");
        }
        if (user.getEnabledStatus() == 1) {
    
    
            throw new LockedAccountException("账号已被锁定,请联系管理员!");
        }
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
        return info;
    }

}

user 用户表结构:

CREATE TABLE `users` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '唯一标识',
  `real_name` varchar(50) NOT NULL DEFAULT '' COMMENT '姓名',
  `name` varchar(50) NOT NULL DEFAULT '' COMMENT '账号',
  `password` varchar(100) NOT NULL DEFAULT '' COMMENT '密码',
  `mobile` varchar(11) NOT NULL COMMENT '手机号',
  `age` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '年龄',
  `enabled_status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '启用状态:0 启用,1 停用',
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `active` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '删除标识:0删除,1存活',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT='用户信息';

用户java对象User:

@Data
@Alias("User")
public class User implements Serializable {
    
    
    /**
     * 唯一标识
     */
    private Long id;
    /**
     * 姓名
     */
    private String realName;
    /**
     * 账号
     */
    private String name;
    /**
     * 密码
     */
    private String password;
    /**
     * 年龄
     */
    private Integer age;
    /**
     * 启用状态:0 启用,1 停用
     */
    private Integer enabledStatus;
    /**
     * 创建时间
     */
    private LocalDateTime createdAt;
    /**
     * 删除标识:0删除,1存活
     */
    private Integer active;
}

ShiroRealm 中为了方便直接使用Mapper查询User,你也可以把UserMapper包装成UserService。


/**
 * @author qizenan
 * @date 2020/11/11 11:42
 */
@Mapper
public interface UserMapper {
    
    
    /**
     * 创建用户信息
     *
     * @param user 实体
     * @return int
     */
    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert(" INSERT INTO users ( real_name ,name ,password ,age ,enabled_status ,created_at ,active) " +
            " VALUES ( #{realName} ,#{name} ,#{password} ,#{age} ,#{enabledStatus} ,NOW() ,1) ")
    int createUser(User user);

    /**
     * 更新用户信息
     *
     * @param user 实体
     * @return int
     */
    @Update(" UPDATE users SET real_name=#{realName},name=#{name},password=#{password},age=#{age},enabled_status=#{enabledStatus} WHERE id=#{id} ")
    int updateUser(User user);

    /**
     * 删除用户信息
     *
     * @param id 唯一标识id
     * @return int
     */
    @Update("UPDATE users SET active=0 WHERE id=#{id}")
    int deleteUser(@Param("id") long id);

    /**
     * 按 账号 查找用户信息
     *
     * @param name 账号
     * @return User
     */
    @Select("SELECT id,real_name,name,password,age,enabled_status,created_at,active FROM users WHERE name=#{name} LIMIT 1 ")
    User getUserByName(@Param("name") String name);

}

5,controller

@Controller
public class LoginController {
    
    

    /**
     * 登录页面
     */
    @GetMapping("/login")
    public String login() {
    
    
        return "login";
    }

    /**
     * 登录
     *
     * @param username   用户账号
     * @param password   用户密码
     * @param rememberMe 是否记住登录:true 是,false 否
     */
    @PostMapping("/login")
    @ResponseBody
    public ResponseBo login(String username, String password) {
    
    
        password = MD5Utils.encrypt(username, password);
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        Subject subject = SecurityUtils.getSubject();
        try {
    
    
            subject.login(token);
            return ResponseBo.ok();
        } catch (UnknownAccountException e) {
    
    
            return ResponseBo.error(e.getMessage());
        } catch (IncorrectCredentialsException e) {
    
    
            return ResponseBo.error(e.getMessage());
        } catch (LockedAccountException e) {
    
    
            return ResponseBo.error(e.getMessage());
        } catch (AuthenticationException e) {
    
    
            return ResponseBo.error("认证失败!");
        }
    }

    /**
     * 默认 / 重定向到index页面
     */
    @RequestMapping("/")
    public String redirectIndex() {
    
    
        return "redirect:/index";
    }

    /**
     * index 页面
     */
    @RequestMapping("/index")
    public String index(Model model) {
    
    
        User user = (User) SecurityUtils.getSubject().getPrincipal();
        model.addAttribute("user", user);
        return "index";
    }

    /**
     * 错误页面
     */
    @GetMapping("/403")
    public String forbid() {
    
    
        return "403";
    }
}
@Controller
@RequestMapping("/user")
public class UserController {
    
    

    @RequiresPermissions("user:list")
    @RequestMapping("list")
    public String userList(Model model) {
    
    
        model.addAttribute("value", "获取用户信息");
        return "user";
    }

    @RequiresPermissions("user:add")
    @RequestMapping("add")
    public String userAdd(Model model) {
    
    
        model.addAttribute("value", "新增用户");
        return "user";
    }

    @RequiresPermissions("user:delete")
    @RequestMapping("delete")
    public String userDelete(Model model) {
    
    
        model.addAttribute("value", "删除用户");
        return "user";
    }
}

6,页面

登录页面 login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8">
	<title>登录</title>
	<link rel="stylesheet" th:href="@{/css/login.css}" type="text/css">
	<script th:src="@{/js/jquery-1.11.1.min.js}"></script>
</head>
<body>
	<div class="login-page">
	  <div class="form">
	      <input type="text" placeholder="用户名" name="username" required="required"/>
	      <input type="password" placeholder="密码" name="password" required="required"/>
	      <button onclick="login()">登录</button>
	  </div>
	</div>
</body>
<script th:inline="javascript"> 
	var ctx = [[@{
     
     /}]];
    function login() {
     
     
    	var username = $("input[name='username']").val();
    	var password = $("input[name='password']").val();
        $.ajax({
     
     
            type: "post",
            url: ctx + "login",
            data: {
     
     "username": username,"password": password},
            dataType: "json",
            success: function (r) {
     
     
                if (r.code == 0) {
     
     
                    location.href = ctx + 'index';
                } else {
     
     
					alert(r.msg);
                }
            }
        });
    }
</script>
</html>

index页面

<!DOCTYPE html>
<html
	xmlns:th="http://www.thymeleaf.org"
	xmlns:shiro="http://www.pollix.at/thymeleaf/shiro" >
<head>
	<meta charset="UTF-8">
	<title>首页</title>
</head>
<style>
	div {
     
     
		border: 1px dashed #ddd;
		padding: 10px;
		margin: 10px 10px 10px 0px;
	}
</style>
<body>
	<p>你好![[${user.realName}]]</p>
	<a th:href="@{/logout}">注销</a>
</body>
</html>

错误页面403

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8">
	<title>暂无权限</title>
</head>
<body>
	<p>您没有权限访问该资源!!</p>
	<a th:href="@{/index}">返回</a>
</body>
</html>

7,测试

访问 http://127.0.0.1:8080/ ,因为初次访问,所以需要登录

在这里插入图片描述
登录成功后

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/fangye1/article/details/109644522