SpringBoot+JWT+Shiro
SpringBoot+JWT+Shiro
SpringBoot+JWT+Shiro简介:本文讲解,如何用SpringBoot整合JWT与Shiro。对于JWT和Shiro的讲解看这两篇文章,本文只讲解,最后的结合的代码。 使用shiro对数据库中的密码进行加密存储(java+springboot+shiro) SpringBoot整合JWT后端代码项目结构后端代码pom.xml代码语言:javas
SpringBoot+JWT+Shiro
简介:本文讲解,如何用SpringBoot整合JWT与Shiro。
对于JWT和Shiro的讲解看这两篇文章,本文只讲解,最后的结合的代码。
使用shiro对数据库中的密码进行加密存储(java+springboot+shiro)
SpringBoot整合JWT
后端代码
项目结构
后端代码
pom.xml
代码语言:javascript代码运行次数:0运行复制 <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusi>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusi>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-crypto-hash</artifactId>
<version>1.11.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>.4.2</version>
</dependency>
<!--引入jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>.4.0</version>
</dependency>
<!--引入mybatis-->
<dependency>
<groupId>spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.</version>
</dependency>
<!--引入lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!--引入druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.19</version>
</dependency>
<!--引入mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.2</version>
</dependency>
</dependencies>
Bean
User
代码语言:javascript代码运行次数:0运行复制@Data // 使用 Lombok 简化实体类代码
@AllArgsCtructor
@oArgsCtructor
public class User {
@TableId(type = IdType.AUTO) // 指定 ID 字段为自增主键
private Long id;
@TableField("username") // 指定该字段映射到数据库表中的 username 列
private String username;
@TableField("password") // 指定该字段映射到数据库表中的 password 列
private String password;
@TableField("salt") // 指定该字段映射到数据库表中的 password 列
private byte[] salt;
public User(String username, String password, byte[] salt) {
this.username = username;
this.password = password;
this.salt = salt;
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
Dto
LoginDto
代码语言:javascript代码运行次数:0运行复制@Data
@AllArgsCtructor
@oArgsCtructor
public class LoginDto {
private String username;
private String password;
}
Config
InterceptorConfig
代码语言:javascript代码运行次数:0运行复制/**
* InterceptorConfig 是一个配置类,用于添加。
* 在这个类中,我们可以配置需要拦截的接口路径以及排除不需要拦截的接口路径。
* 在这个例子中,我们添加了JWTInterceptor来对请求进行token验证,
* 并设置了"/user/test"接口需要进行验证,而"/user/login"接口则被排除在验证之外,即所有用户都放行登录接口。
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
/**
* 添加配置
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/user/test", "/user/all") // 对"/user/test"接口进行token验证
.excludePathPatterns("/user/login", "/user/register"); // 所有用户都放行登录接口
}
}
Result
代码语言:javascript代码运行次数:0运行复制@Data
@oArgsCtructor
@AllArgsCtructor
public class Result<T> {
private int code;
private String message;
private T data;
public Result(T data) {
= 200;
= "success";
this.data = data;
}
public Result(T data, boolean success, String message) {
if (success) {
= 200;
= "success";
} else {
= 500; // 自定义错误状态码(示例为500)
= message;
}
this.data = data;
}
public Result(int code, String message) {
= code;
= message;
this.data = null;
}
public static <T> Result<T> success(String message) {
return new Result<>(200, message);
}
/**
* 返回执行失败的结果(默认状态码为500)
*
* @param message 提示信息
* @return 失败的结果对象
*/
public static <T> Result<T> fail(String message) {
return new Result<>(500, message);
}
/**
* 返回执行失败的结果(自定义状态码和提示信息)
*
* @param code 状态码
* @param message 提示信息
* @return 失败的结果对象
*/
public static <T> Result<T> fail(int code, String message) {
return new Result<>(code, message);
}
}
Controller
UserController
代码语言:javascript代码运行次数:0运行复制@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private IUserService userService;
@PostMapping("/login")
public Result<Map<String, Object>> login(@RequestBody User user) {
// 打印用户名和密码
println(user);
// 创建结果对象
Result<Map<String, Object>> result;
try {
// 调用userService的login方法进行用户认证
User userDB = userService.login(user);
// 获取用户ID和用户名,并将其放入payload
Map<String, String> payload = new HashMap<>();
payload.put("id", userDB.getId().toString());
payload.put("name", userDB.getUsername());
// 生成JWT的令牌
String token = JWTUtils.getToken(payload);
// 构造成功的结果对象
result = new Result<>(200, "认证成功");
result.setData(new HashMap<>());
result.getData().put("token", token); // 响应token
} catch (Exception e) {
// 构造失败的结果对象
result = Result.fail(500, e.getMessage());
}
return result;
}
@PostMapping("/register")
public Result<Map<String, Object>> register(@RequestBody User user){
println("注册");
println(user);
// 创建结果对象
Result<Map<String, Object>> result;
User user1 = userService.selectByUsername(user.getUsername());
if (user1 != null){
result = Result.fail(405, "用户已存在,请更换用户名");
} else {
boolean register = userService.register(user.getUsername(), user.getPassword());
if (register){
result = new Result(user);
} else {
result = Result.fail(500, "注册失败");
}
}
return result;
}
@GetMapping("/all/{token}")
Result<Map<String, Object>> getByAll(@PathVariable String token) {
List<User> list = userService.list();
Result<Map<String, Object>> result;
println(token);
try {
Map<String, Object> map = new HashMap<>();
// 处理自己的业务逻辑
// 校验并解析token
verifyToken(token);
map.put("data", list);
// 构造成功的结果对象
result = new Result<>(200, "请求成功!");
result.setData(map);
} catch (Exception e) {
// 构造失败的结果对象
result = Result.fail(500, e.getMessage());
}
return result;
}
private void verifyToken(String token) throws Exception {
// 校验并解析token
DecodedJWT verify = JWTUtils.verify(token);
// 可以在这里添加其他的校验逻辑
// 打印解析出的用户id和用户名
log.info("用户id: [{}]", verify.getClaim("id").asString());
log.info("用户name: [{}]", verify.getClaim("name").asString());
}
@GetMapping("/test/{token}")
public Result<Map<String, Object>> test(@PathVariable String token) {
// 创建结果对象
Result<Map<String, Object>> result;
try {
Map<String, Object> map = new HashMap<>();
// 处理自己的业务逻辑
// 从请求头中获取token
// 校验并解析token
DecodedJWT verify = JWTUtils.verify(token);
// 打印解析出的用户id和用户名
log.info("用户id: [{}]", verify.getClaim("id").asString());
log.info("用户name: [{}]", verify.getClaim("name").asString());
// 构造成功的结果对象
result = new Result<>(200, "请求成功!");
result.setData(map);
} catch (Exception e) {
// 构造失败的结果对象
result = Result.fail(500, e.getMessage());
}
return result;
}
}
Interceptor
JWTInterceptor
代码语言:javascript代码运行次数:0运行复制/**
* JWTInterceptor是一个,用于验证请求头中的JWT令牌是否有效。
* 当有请求进入时,该会首先从请求头中获取令牌,并尝试验证其有效性。
* 如果令牌验证成功,则放行请求;否则,拦截请求并返回相应的错误信息。
*/
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletRespe respe, Object handler) throws Exception {
// 创建一个Map对象,用于存储响应信息
Map<String, Object> map = new HashMap<>();
// 从请求头中获取令牌
String token = request.getHeader("token");
try {
JWTUtils.verify(token); // 验证令牌的有效性
return true; // 放行请求
} catch (SignatureVerificationException e) {
e.printStackTrace();
map.put("msg", "无效签名!");
} catch (TokenExpiredException e) {
e.printStackTrace();
map.put("msg", "token过期!");
} catch (AlgorithmMismatchException e) {
e.printStackTrace();
map.put("msg", "token算法不一致!");
} catch (Exception e) {
e.printStackTrace();
map.put("msg", "token无效!!");
}
map.put("state", false); // 设置状态为false
// 将Map转化为JSO字符串(使用Jackson库)
String json = new ObjectMapper().writeValueAsString(map);
respe.setContentType("application/json;charset=UTF-8"); // 设置响应的Content-Type
respe.getWriter().println(json); // 将JSO字符串写入响应中
return false; // 不放行请求
}
}
Mapper
UserMapper
代码语言:javascript代码运行次数:0运行复制@Mapper
public interface UserMapper extends BaseMapper<User> {
@Select("select * from user where username = #{username} and password = #{password}")
User login(@Param("username") String username, @Param("password") String password);
@Select("select * from user")
List<User> list();
@Select("select * from user where username = #{username}")
User selectByUsername(@Param("username")String username);
}
Service
IUserService
代码语言:javascript代码运行次数:0运行复制public interface IUserService extends IService<User> {
User login(LoginDto user);//登录接口
User selectByUsername(String username); // 查询用户的名字
boolean register(User user); // 注册接口
}
UserServiceImpl
代码语言:javascript代码运行次数:0运行复制@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public User login(LoginDto user) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
("username", user.getUsername());
User users = getOne(wrapper);
if (users == null) {
// 如果用户不存在,则认为登录失败
return null;
}
String hashedPassword = users.getPassword();
byte[] salt = users.getSalt();
// 对用户输入的密码进行加密处理,并将结果与数据库中的哈希值比较
String hashedInputPassword = hash(user.getPassword(), salt);
println(hashedPassword);
println(salt);
println(hashedInputPassword);
if ((hashedInputPassword)){ // 如果密码一致
return users;
} else {
return null;
}
}
@Override
public User selectByUsername(String username) {
return userMapper.selectByUsername(username);
}
@Override
public boolean register(User user) {
// 生成盐值
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
// 对密码进行加密处理
String hashPassword = hash(user.getPassword(), salt);
// 将用户名、盐值和哈希后的密码保护到数据库中
User user1 = new User(user.getUsername(), hashPassword, salt);
println(user1);
boolean success = save(user1);
println(userMapper.list());
return success;
}
@Override
public boolean saveBatch(Collection<User> entityList, int batchSize) {
return super.saveBatch(entityList, batchSize);
}
private String hash(String password, byte[] salt) {
int iterati = 10000;
int keyLength = 256;
PBEKeySpec spec = new PBEKeySpec((), salt, iterati, keyLength);
SecretKeyFactory skf;
try {
skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = skf.generateSecret(spec).getEncoded();
return Base64.getEncoder().encodeToString(hash);
} catch (oSuchAlgorithmException | InvalidKeySpecException e) {
throw new RuntimeException(e);
}
}
}
Utils
代码语言:javascript代码运行次数:0运行复制public class JWTUtils {
private static final String SIG = "!Q@We4r%T^Y";
/**
* 生成token header.payload.sing
*/
public static String getToken(Map<String,String> map){
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE,7);//默认7天过期
//创建jwt builder
JWTCreator.Builder builder = ();
//payload
map.forEach((k,v)->{
builder.withClaim(k,v);
});
String token = builder.withExpiresAt(instance.getTime())//指定令牌过期时间
.sign(Algorithm.HMAC256(SIG));//sign
return token;
}
/**
* 验证token 合法性
*
*/
public static DecodedJWT verify(String token){
return JWT.require(Algorithm.HMAC256(SIG)).build().verify(token);
}
// /**
// * 获取token信息方法
// */
// public static DecodedJWT getTokenInfo(String token){
// DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIG)).build().verify(token);
// return verify;
// }
}
接口测试
#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格
上传时间: 2025-07-19 17:26:02
下一篇:Vue实现一个悬浮加号按钮
推荐阅读
留言与评论(共有 5 条评论) |
本站网友 电子白板软件 | 21分钟前 发表 |
String password | |
本站网友 博士家园 | 15分钟前 发表 |
* 当有请求进入时 | |
本站网友 脸上青春痘怎么治疗 | 5分钟前 发表 |
而"/user/login"接口则被排除在验证之外 | |
本站网友 学府一号 | 12分钟前 发表 |
salt); println(user1); boolean success = save(user1); println(userMapper.list()); return success; } @Override public boolean saveBatch(Collection<User> entityList |