5.1. 获取用户名
5.2. 调用 service 成判断用户是否存在
5.3. 判断用户的状态
5.4. 返回一个 SimpleAuthenticationInfo 认证对象
6.1 配置 ShiroFilterFactoryBean 注入 SecurityManager
6.2 配置 SecurityManager 注入咱们自定义的认证处理器(ShiroRealm)
6.3. 配置自定义认证处理器(ShiroRealm)注入自定义明码匹配器(CredentialsMatcher)
7.1 自定义加密解密工具类实现
7.2 应用 BCryptPasswordEncoder 类实现
8.1MySQL 数据源
8.2 通用 mapper 的代理类门路
<?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>
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("增加胜利");
}
}
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());
}
}
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;
}
}
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;
}
}
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());
}
}