上一篇文章中介绍了Shiro 查看

将Shiro集成到spring boot的步骤:

  (1)定义一个ShiroConfig,配置SecurityManager Bean,SecurityManager为Shiro的安全管理器,管理着所有Subject

  (2)在ShiroConfig中配置ShiroFilterFactoryBean,其为Shiro过滤器工厂类,依赖于SecurityManager

  (3)自定义Realm实现,Realm包含doGetAuthorizationInfo()doGetAuthenticationInfo()方法

实现下用户认证:

  没登录跳转到登录页面;登录后跳转到首页面;注销后,跳转到登录页面

1.引入依赖

  引入Shiro和thymeleaf依赖

<!-- thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency> <!-- shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>

2.ShiroConfig

  定义一个Shiro配置类,ShiroConfig

package com.sfn.bms.common.shiro;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.*; /**
* Shiro 配置类
*/
@Configuration
public class ShiroConfig { @Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置securityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 登录的url
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后跳转的url
shiroFilterFactoryBean.setSuccessUrl("/index");
// 未授权url
shiroFilterFactoryBean.setUnauthorizedUrl("/403"); LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // 定义filterChain,静态资源不拦截
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
// druid数据源监控页面不拦截
filterChainDefinitionMap.put("/druid/**", "anon");
// 配置退出过滤器,其中具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/", "anon");
// 除上以外所有url都必须认证通过才可以访问,未通过认证自动访问LoginUrl
filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
} @Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
} @Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm shiroRealm = new MyShiroRealm();
return shiroRealm;
} }

  anonauthc等为Shiro为实现的过滤器

3.Realm

  Realm进行实现,然后注入到SecurityManager中

  自定义Realm实现只需继承AuthorizingRealm类,然后实现doGetAuthorizationInfo()和doGetAuthenticationInfo()方法即可

  用户认证只实现doGetAuthenticationInfo()

package com.sfn.bms.common.shiro;
import com.sfn.bms.system.model.User;
import com.sfn.bms.system.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; /**
* 自定义实现 ShiroRealm,包含认证和授权两大模块
*/
@Component("shiroRealm")
public class MyShiroRealm extends AuthorizingRealm { @Autowired
private UserService userService; /**
* 授权模块,获取用户角色和权限
*
* @param principal principal
* @return AuthorizationInfo 权限信息
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) { return null;
} /**
* 用户认证
*
* @param token AuthenticationToken 身份认证 token
* @return AuthenticationInfo 身份认证信息
* @throws AuthenticationException 认证相关异常
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 获取用户输入的用户名和密码
String account = (String) token.getPrincipal();
String password = new String((char[]) token.getCredentials()); // 通过用户名到数据库查询用户信息
User user = userService.findByAccount(account); if (user == null) {
throw new UnknownAccountException("用户名或密码错误!");
}
if (!password.equals(user.getPassword())) {
throw new IncorrectCredentialsException("用户名或密码错误!");
}
if (user.getStatus().equals("0")) {
throw new LockedAccountException("账号已被锁定,请联系管理员!");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
return info;
} }

4.数据层

  数据表User

  实体类User

package com.sfn.bms.system.model;

import javax.persistence.*;

public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Short id; /**
* 账号
*/
private String account; /**
* 密码
*/
private String password; /**
* 邮箱
*/
private String email; /**
* 状态 1-正常,0-禁用,-1-删除
*/
private Boolean status; /**
* 添加时间
*/
@Column(name = "create_time")
private Integer createTime; /**
* 上次登陆时间
*/
@Column(name = "last_login_time")
private Integer lastLoginTime; /**
* 上次登录IP
*/
@Column(name = "last_login_ip")
private String lastLoginIp; /**
* 登陆次数
*/
@Column(name = "login_count")
private Integer loginCount; /**
* @return id
*/
public Short getId() {
return id;
} /**
* @param id
*/
public void setId(Short id) {
this.id = id;
} /**
* 获取账号
*
* @return account - 账号
*/
public String getAccount() {
return account;
} /**
* 设置账号
*
* @param account 账号
*/
public void setAccount(String account) {
this.account = account == null ? null : account.trim();
} /**
* 获取密码
*
* @return password - 密码
*/
public String getPassword() {
return password;
} /**
* 设置密码
*
* @param password 密码
*/
public void setPassword(String password) {
this.password = password == null ? null : password.trim();
} /**
* 获取邮箱
*
* @return email - 邮箱
*/
public String getEmail() {
return email;
} /**
* 设置邮箱
*
* @param email 邮箱
*/
public void setEmail(String email) {
this.email = email == null ? null : email.trim();
} /**
* 获取状态 1-正常,0-禁用,-1-删除
*
* @return status - 状态 1-正常,0-禁用,-1-删除
*/
public Boolean getStatus() {
return status;
} /**
* 设置状态 1-正常,0-禁用,-1-删除
*
* @param status 状态 1-正常,0-禁用,-1-删除
*/
public void setStatus(Boolean status) {
this.status = status;
} /**
* 获取添加时间
*
* @return create_time - 添加时间
*/
public Integer getCreateTime() {
return createTime;
} /**
* 设置添加时间
*
* @param createTime 添加时间
*/
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
} /**
* 获取上次登陆时间
*
* @return last_login_time - 上次登陆时间
*/
public Integer getLastLoginTime() {
return lastLoginTime;
} /**
* 设置上次登陆时间
*
* @param lastLoginTime 上次登陆时间
*/
public void setLastLoginTime(Integer lastLoginTime) {
this.lastLoginTime = lastLoginTime;
} /**
* 获取上次登录IP
*
* @return last_login_ip - 上次登录IP
*/
public String getLastLoginIp() {
return lastLoginIp;
} /**
* 设置上次登录IP
*
* @param lastLoginIp 上次登录IP
*/
public void setLastLoginIp(String lastLoginIp) {
this.lastLoginIp = lastLoginIp == null ? null : lastLoginIp.trim();
} /**
* 获取登陆次数
*
* @return login_count - 登陆次数
*/
public Integer getLoginCount() {
return loginCount;
} /**
* 设置登陆次数
*
* @param loginCount 登陆次数
*/
public void setLoginCount(Integer loginCount) {
this.loginCount = loginCount;
}
}

  UserService

package com.sfn.bms.system.service;

import com.sfn.bms.common.service.IService;
import com.sfn.bms.system.model.User; public interface UserService extends IService<User> {
User findByAccount(String account);
}

  UserServiceImpl

package com.sfn.bms.system.service.impl;

import com.sfn.bms.common.service.impl.BaseService;
import com.sfn.bms.system.model.User;
import com.sfn.bms.system.service.UserService;
import org.springframework.stereotype.Repository;
import tk.mybatis.mapper.entity.Example; import java.util.List; @Repository("userService")
public class UserServiceImpl extends BaseService<User> implements UserService {
@Override
public User findByAccount(String account) {
Example example = new Example(User.class);
example.createCriteria().andCondition("lower(account)=", account.toLowerCase());
List<User> list = this.selectByExample(example);
return list.isEmpty() ? null : list.get(0);
} }

  UserMapper.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.sfn.bms.system.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.sfn.bms.system.model.User">
<!--
WARNING - @mbg.generated
-->
<id column="id" jdbcType="SMALLINT" property="id" />
<result column="account" jdbcType="VARCHAR" property="account" />
<result column="password" jdbcType="CHAR" property="password" />
<result column="email" jdbcType="VARCHAR" property="email" />
<result column="status" jdbcType="BIT" property="status" />
<result column="create_time" jdbcType="INTEGER" property="createTime" />
<result column="last_login_time" jdbcType="INTEGER" property="lastLoginTime" />
<result column="last_login_ip" jdbcType="VARCHAR" property="lastLoginIp" />
<result column="login_count" jdbcType="INTEGER" property="loginCount" />
</resultMap> </mapper>

5.前端页面

  login.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" th:href="@{/css/login.css}" type="text/css">
<link rel="stylesheet" th:href="@{css/iCheck/minimal/blue.css}" type="text/css">
<link rel="stylesheet" th:href="@{css/app.css}" type="text/css">
<script th:src="@{/js/jquery.min.js}"></script>
</head>
<body>
<div class="login-page">
<!-- Form-->
<div class="form">
<div class="form-toggle"></div>
<div class="form-panel one">
<div class="form-header">
<h1>账户登录</h1>
</div>
<div class="form-content">
<div class="form-group">
<label>用户名</label>
<input type="text" name="account" />
</div>
<div class="form-group">
<label>密码</label>
<input type="password" name="password" />
</div>
<div class="form-group">
<button onclick="login()" id="loginButton">登录</button>
</div>
</div>
</div>
</div> </div>
</body>
<script th:inline="javascript">
var ctx = [[@{/}]];
function login() {
var account= $("input[name='account']").val();
var password = $("input[name='password']").val();
$.ajax({
type: "post",
url: ctx + "login",
data: {"account": account,"password": password},
dataType: "json",
success: function (r) {
if (r.code == 0) {
location.href = ctx + 'index';
} else {
alert(r.msg);
}
}
});
}
</script>
</html>

  index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>你好![[${user.account}]]</p>
<a th:href="@{/logout}">注销</a>
</body>
</html>

6.Controller方法

LoginController
package com.sfn.bms.system.controller;

import com.sfn.bms.common.domian.ResponseBo;
import com.sfn.bms.common.util.MD5Utils;
import com.sfn.bms.system.model.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; @Controller
public class LoginController { @GetMapping("/login")
public String login() {
return "login";
} @PostMapping("/login")
@ResponseBody
public ResponseBo login(String account, String password) {
// 密码MD5加密
password = MD5Utils.encrypt(account, password);
System.out.println(password);
UsernamePasswordToken token = new UsernamePasswordToken(account, password);
// 获取Subject对象
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("认证失败!");
}
} @RequestMapping("/")
public String redirectIndex() {
return "redirect:/index";
} @RequestMapping("/index")
public String index(Model model) {
// 登录成后,即可通过Subject获取登录的用户信息
User user = (User) SecurityUtils.getSubject().getPrincipal(); model.addAttribute("user", user);
return "index";
}
}

7.测试

  启动项目

  浏览器打开

  http://localhost:8080/

  http://localhost:8080/index

  回跳转到http://localhost:8080/login

  输入用户名super密码123456

点注销,根据ShiroConfig的配置filterChainDefinitionMap.put("/logout", "logout"),Shiro会自动帮我们注销用户信息,并重定向到/路径

相关代码 地址

Spring boot后台搭建二集成Shiro实现用户验证的更多相关文章

  1. Spring boot后台搭建二集成Shiro权限控制

    上一篇文章,实现了用户验证 查看,接下来实现下权限控制 权限控制,是管理资源访问的过程,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等 Apache Shir ...

  2. Spring boot后台搭建二集成Shiro添加Remember Me

    上一片文章实现了用户验证  查看 当用户成功登录后,关闭浏览器,重新打开浏览器访问http://localhost:8080,页面会跳转到登录页,因为浏览器的关闭后之前的登录已失效 Shiro提供了R ...

  3. Spring boot后台搭建一使用MyBatis集成Mapper和PageHelper

    目标: 使用 Spring  boot+MyBatis+mysql 集成 Mapper 和 PageHelper,实现基本的增删改查 先建一个基本的 Spring Boot 项目开启 Spring B ...

  4. 七、spring boot 1.5.4 集成shiro+cas,实现单点登录和权限控制

    1.安装cas-server-3.5.2 官网:https://github.com/apereo/cas/releases/tag/v3.5.2 下载地址:cas-server-3.5.2-rele ...

  5. Spring boot后台搭建二为Shiro权限控制添加缓存

    在添加权限控制后,添加方法 查看 当用户访问”获取用户信息”.”新增用户”和”删除用户”的时,后台输出打印如下信息 , Druid数据源SQL监控 为了避免频繁访问数据库获取权限信息,在Shiro中加 ...

  6. Spring Boot 2.X(二):集成 MyBatis 数据层开发

    MyBatis 简介 概述 MyBatis 是一款优秀的持久层框架,支持定制化 SQL.存储过程以及高级映射.它采用面向对象编程的方式对数据库进行 CRUD 的操作,使程序中对关系数据库的操作更方便简 ...

  7. Spring Boot+CXF搭建WebService(转)

    概述 最近项目用到在Spring boot下搭建WebService服务,对Java语言下的WebService了解甚少,而今抽个时间查阅资料整理下Spring Boot结合CXF打架WebServi ...

  8. spring boot+mybatis搭建项目

    一.创建spring boot项目 1.File->New->Project 2.选择 Spring Initializr ,然后选择默认的 url 点击[Next]: 3.修改项目信息 ...

  9. spring boot / cloud (十二) 异常统一处理进阶

    spring boot / cloud (十二) 异常统一处理进阶 前言 在spring boot / cloud (二) 规范响应格式以及统一异常处理这篇博客中已经提到了使用@ExceptionHa ...

随机推荐

  1. 实现批量添加10个用户,用户名为user01-10,密码为user后面跟3个随机字符

    #!/bin/bash ` do user="user$i" password=$( | md5sum | ) useradd user$i echo "$user$pa ...

  2. 项目Alpha冲刺--8/10

    项目Alpha冲刺--8/10 作业要求 这个作业属于哪个课程 软件工程1916-W(福州大学) 这个作业要求在哪里 项目Alpha冲刺 团队名称 基于云的胜利冲锋队 项目名称 云评:高校学生成绩综合 ...

  3. 使用 Word 写作论文时设置格式技巧记录

    这里主要记录使用 Word 2013 版本的 Microsoft office Word 软件进行论文书写时的一些常用的格式设置技巧,以供分享与记录. Word文档页脚添加页码 Word设置多级标题格 ...

  4. presto docker简单试用

    starburstdata 团队提供了一个docker 版本的presto,其中已经内置了几个connectors tpch tpcds memory backhole jmx system pull ...

  5. Mongo 安装及基本操作

    一. 安装 Mongo文档: https://docs.mongodb.com/v3.6/administration/install-enterprise-linux/ Linux mongo的配置 ...

  6. 钟长者P71解题报告

    T1 [题目描述] 给你N个字符串,你每次可以选择其中一个字符串的一段前缀进行翻转,但是你必须保证这个前缀的长度是偶数.你可以进行无限次这样的操作,并且如果两个字符串变得相同的时候,你就可以把这两个字 ...

  7. 洛谷 P2918 [USACO08NOV]买干草Buying Hay 题解

    P2918 [USACO08NOV]买干草Buying Hay 题目描述 Farmer John is running out of supplies and needs to purchase H ...

  8. Angular2发送HTTP请求SpringBoot后台跨域问题解决

    Angular通过http发送post请求至SpringBoot的Controller,由于同源策略的保护,遇到跨域问题: • 源(origin)就是协议(http).域名(localhost)和端口 ...

  9. 怎么样使element ui 的table某列变色

    第一步.在el-table里面加上:row-style="rowClass" <el-table :data="targetCarList" border ...

  10. PL/SQL Developer插入数据到数据库出现数据中文乱码

    问题描述: 使用PL/SQL Developer往Oracle数据库插入数据,出现中文乱码! 解决办法: 1.执行脚本 select userenv('language') from dual; 结果 ...