开发步骤
1.导入相关依赖(一共10个)
2.控制器编写2个方法(注册和登录)
3.自定义认证授权处理器,继承AuthorizingRealm类
4.实现doGetAuthenticationInfo认证方法
5.处理认证方法
5.1.获取用户名
5.2.调用service成判断用户是否存在
5.3.判断用户的状态
5.4.返回一个SimpleAuthenticationInfo认证对象
6.编写shiro配置类(ShiroConfig)
6.1配置ShiroFilterFactoryBean注入SecurityManager
6.2配置SecurityManager注入我们自定义的认证处理器(ShiroRealm)
6.3.配置自定义认证处理器(ShiroRealm)注入自定义密码匹配器(CredentialsMatcher)
7.自定义密码匹配器(继承SimpleCredentialsMatcher类),重写doCredentialsMatch方法
7.1自定义加密解密工具类实现
7.2使用BCryptPasswordEncoder类实现
8.配置application.yml文件
8.1MySQL数据源
8.2通用mapper 的代理类路径
9.异常信息相应处理(可有可无)
10.测试功能,注册和登录
1.pom依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<!--测试启动器-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--springMVC-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--整合Mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--mysql数据源-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--通用mapper-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<!--spring整合shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!--日志打印-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--lombok懒人依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--JSON转换-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.71</version>
</dependency>
<!--加密-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
<version>5.3.3.RELEASE</version>
</dependency>
</dependencies>
</project>
2.controller的2个方法:添加和登录
package com.huacheng.controller;
import com.huacheng.domain.User;
import com.huacheng.response.ResponseVO;
import com.huacheng.service.UserService;
import com.huacheng.util.PasswordUtil;
import com.huacheng.util.ResultUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Log4j2
@RestController
public class LoginController {
@Autowired
private UserService userService;
/**
* 登录
*
* @param username
* @param password
* @param rememberMe
* @return
*/
@RequestMapping("/login")
public ResponseVO login(String username, String password, Boolean rememberMe) {
UsernamePasswordToken token = null;
try {
//登录
token = new UsernamePasswordToken(username, password, false);
//获取当前的登录对象
Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);
return ResultUtil.success("登录成功:"+username+"用户欢迎您");
} catch (Exception e) {
log.info("登录失败用户名{}", username, e);
token.clear();
return ResultUtil.error(e.getMessage());
}
}
@RequestMapping("/add")
public ResponseVO add(User user) {
try {
userService.add(user);
} catch (Exception e) {
log.info("新增失败:{}", user);
return ResultUtil.error("添加失败");
}
log.info("新增用户:{}", user);
return ResultUtil.success("添加成功");
}
}
3.编写自定义认证授权处理器(ShiroRealm)
package com.huacheng.realms;
import com.huacheng.domain.User;
import com.huacheng.service.UserService;
import com.huacheng.util.PasswordUtil;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
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.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.print.DocFlavor;
@Log4j2
//@Component
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* 授权的方法
*
* @param principal
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
return null;
}
/**
* 认证的方法
*
* @param token
* @return
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
//1.获取用户名
String username = (String) token.getPrincipal();
User user = userService.findUserByUsername(username);
//2.判断用户的账号是否存在
if (user == null) {
log.info("用户名或者密码错误user:{}", user);
throw new UnknownAccountException("用户名或者密码错误");
}
//3.判断用户的状态是否可用
if (user.getStatus() == 0) {
log.info("帐号已被锁定,禁止登录,user:{}", user);
throw new LockedAccountException("帐号已被锁定,禁止登录!");
}
//4.返回一个认证对象
log.info("登录成功:{}", user.getUsername() + "用户欢迎您");
return new SimpleAuthenticationInfo(user.getId(),
user.getPassword(),
getName());
}
}
4.编写shiro配置类(ShiroConfig)
package com.huacheng.config;
import com.huacheng.credentials.CredentialsMatcher;
import com.huacheng.realms.ShiroRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
/* // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index.html");*/
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager(ShiroRealm myRealms) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//注入我们自定义的认证授权处理器
securityManager.setRealm(myRealms);
return securityManager;
}
@Bean
public ShiroRealm shiroRealm(CredentialsMatcher credentialsMatcher) {
ShiroRealm myRealms = new ShiroRealm();
//注入自定义的密码匹配器
myRealms.setCredentialsMatcher(credentialsMatcher);
return myRealms;
}
}
5.自定义密码匹配器(这里我用2中方式都可行)
package com.huacheng.credentials;
import com.huacheng.util.PasswordUtil;
import lombok.extern.java.Log;
import lombok.extern.log4j.Log4j2;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
/**
* Shiro-密码凭证匹配器(验证密码有效性)
*/
@Log4j2
@Component
public class CredentialsMatcher extends SimpleCredentialsMatcher {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
UsernamePasswordToken utoken = (UsernamePasswordToken) token;
//1.获得用户输入的明文密码
String loginPassword = new String(utoken.getPassword());
//2.获得数据路的密文密码
String dbPassword = (String) info.getCredentials();
//3.进行密码的比对
//第一种方式,使用自己的加密解密方法比对密码
/* String decryptPassword = null;
try {
decryptPassword = PasswordUtil.decrypt(dbPassword, utoken.getUsername());
} catch (Exception e) {
log.info("密码解密失败{}", loginPassword);
throw new RuntimeException("密码解密失败");
}
if (!decryptPassword.equals(loginPassword)) {
throw new IncorrectCredentialsException("用户名或者密码错误");
}*/
//第二种方式,使用BCryptPasswordEncoder进行解密
if (!passwordEncoder.matches(loginPassword, dbPassword)) {
throw new IncorrectCredentialsException("用户名或者密码错误");
}
return true;
}
}
6.统一异常处理的控制器
package com.huacheng.controller;
import com.huacheng.enums.ResponseStatus;
import com.huacheng.exception.Myexception;
import com.huacheng.response.ResponseVO;
import com.huacheng.util.CommonConst;
import com.huacheng.util.ResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.lang.reflect.UndeclaredThrowableException;
/**
* 统一异常处理类<br>
* 捕获程序所有异常,针对不同异常,采取不同的处理方式
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @website https://www.zhyd.me
* @date 2018/4/24 14:37
* @since 1.0
*/
@Slf4j
@ControllerAdvice
public class ExceptionHandleController {
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ResponseVO handle(Throwable e) {
if (e instanceof Myexception) {
return ResultUtil.error(e.getMessage());
}
if (e instanceof UndeclaredThrowableException) {
e = ((UndeclaredThrowableException) e).getUndeclaredThrowable();
}
ResponseStatus responseStatus = ResponseStatus.getResponseStatus(e.getMessage());
if (responseStatus != null) {
log.error(responseStatus.getMessage());
return ResultUtil.error(responseStatus.getCode(), responseStatus.getMessage());
}
e.printStackTrace(); // 打印异常栈
return ResultUtil.error(CommonConst.DEFAULT_ERROR_CODE, ResponseStatus.ERROR.getMessage());
}
}
7.使用postman测试
测试添加

测试登录


**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。