开发步骤
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@RestControllerpublic 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//@Componentpublic 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;@Configurationpublic 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@Componentpublic 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@ControllerAdvicepublic 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()); }}