新建springboot项目,导入依赖
<?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.7.16</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.shrimpking</groupId>
<artifactId>springboot-67</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-67</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-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- springboot整合方式的依赖包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<!-- 引入jsp解析的依赖 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--swagger依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!--swagger ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.properties
server.port=8089
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimeZone=UTC
spring.datasource.username=root
spring.datasource.password=mysql123
#日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#配置别名
mybatis-plus.type-aliases-package=com.shrimpking.pojo
#swagger
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
#日期格式化
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone= GMT+8
#jsp
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
mysql数据库
drop table if exists af_user;
create table af_user(
id int not null auto_increment primary key comment '主键,自增',
username varchar(50) not null comment '账号',
`password` varchar(50) not null comment '密码',
salt varchar(100) not null comment '加密盐'
) comment '用户表';
pojo
user.java
package com.shrimpking.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* <p>
* 用户表
* </p>
*
* @author shrimpking
* @since 2023-10-03
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("af_user")
@ApiModel(value="User对象", description="用户表")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "主键,自增")
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ApiModelProperty(value = "账号")
private String username;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "加密盐")
private String salt;
}
mapper
usermapper.java
package com.shrimpking.mapper;
import com.shrimpking.pojo.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* 用户表 Mapper 接口
* </p>
*
* @author shrimpking
* @since 2023-10-03
*/
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
mapperxml
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.shrimpking.mapper.UserMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.shrimpking.pojo.User">
<id column="id" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
<result column="salt" property="salt" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, username, password, salt
</sql>
</mapper>
service
userservice.java
package com.shrimpking.service;
import com.shrimpking.pojo.User;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 用户表 服务类
* </p>
*
* @author shrimpking
* @since 2023-10-03
*/
public interface UserService extends IService<User> {
//注册用户
boolean register(User user);
//根据用户名查询用户实体
User getUserByUserName(String username);
}
serviceimpl
userservieimpl.java
package com.shrimpking.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.shrimpking.pojo.User;
import com.shrimpking.mapper.UserMapper;
import com.shrimpking.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.shrimpking.utils.SaltUtils;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 用户表 服务实现类
* </p>
*
* @author shrimpking
* @since 2023-10-03
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public boolean register(User user)
{
//处理登录业务层
//明文密码进行md5 + 盐 + 散列次数
//生成随机盐
String salt = SaltUtils.getSalt(4);
//加密
Md5Hash md5Hash = new Md5Hash(user.getPassword(),salt,1024);
//保存盐
user.setSalt(salt);
//保存加密的密码
//
user.setPassword(md5Hash.toHex());
int insert = this.userMapper.insert(user);
if(insert > 0) return true;
return false;
}
@Override
public User getUserByUserName(String username)
{
//数据库中的username字段没有设置唯一键,使用selecOne会报错,所以返回列表
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getUsername,username);
queryWrapper.orderByDesc(User::getId);
List<User> userList = this.userMapper.selectList(queryWrapper);
return userList.get(0);
}
}
controller
testcontroller.java
package com.shrimpking.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* Created by IntelliJ IDEA.
*
* @Author : Shrimpking
* @create 2023/10/2 22:18
*/
@Controller
@RequestMapping("/test")
public class TestController
{
/**
* 登录
* @param username
* @param password
* @return
*/
@PostMapping("/login")
public String login(String username,String password){
//获取主体对象
Subject subject = SecurityUtils.getSubject();
//生成令牌token
AuthenticationToken token = new UsernamePasswordToken(username,password);
//
try
{
subject.login(token);
return "redirect:/index.jsp";
}
catch (UnknownAccountException e)
{
e.printStackTrace();
System.out.println("用户不存在");
}
catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("密码错误");
}
return "redirect:/login.jsp";
}
/**
* 退出
* @return
*/
@GetMapping("/logout")
public String logout(){
//获取身份主体
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "redirect:/login.jsp";
}
}
usercontroller.java
package com.shrimpking.controller;
import com.shrimpking.pojo.User;
import com.shrimpking.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 用户表 前端控制器
* </p>
*
* @author shrimpking
* @since 2023-10-03
*/
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public String register(User user){
boolean register = this.userService.register(user);
if(register) return "redirect:/login.jsp";
return "redirect:/register.jsp";
}
}
config
shiroconfig.java
package com.shrimpking.config;
import com.shrimpking.shiro.realms.CustomerRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.realm.Realm;
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;
import java.util.HashMap;
import java.util.Map;
/**
* Created by IntelliJ IDEA.
*
* @Author : Shrimpking
* @create 2023/10/2 21:30
* shiro的配置类
*/
@Configuration
public class ShiroConfig
{
//创建shirofilter
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
//负责拦截请求
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
factoryBean.setSecurityManager(defaultWebSecurityManager);
//配置系统受限资源
Map<String,String> map = new HashMap<>();
//配置系统公共资源
map.put("/test/login","anon");
map.put("/register.jsp","anon");
map.put("/user/register","anon");
map.put("/swagger-ui.html#/","anon");
//请求这个资源需要认证和授权后
map.put("/**","authc");
factoryBean.setFilterChainDefinitionMap(map);
//
factoryBean.setLoginUrl("/login.jsp");
return factoryBean;
}
//创建安全管理器
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(Realm realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置realm
securityManager.setRealm(realm);
return securityManager;
}
//创建自定义realm
@Bean
public Realm realm(){
CustomerRealm customerRealm = new CustomerRealm();
//设置加密凭证匹配器
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
//使用md5加密
matcher.setHashAlgorithmName("MD5");
//散列次数
matcher.setHashIterations(1024);
//设置匹配器
customerRealm.setCredentialsMatcher(matcher);
return customerRealm;
}
}
swaggerconfig.java
package com.shrimpking.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Created by IntelliJ IDEA.
*
* @Author : Shrimpking
* @create 2023/9/10 14:58
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig
{
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any()).build();
}
private ApiInfo apiInfo(){
return new ApiInfoBuilder().build();
}
}
realms
package com.shrimpking.shiro.realms;
import com.shrimpking.pojo.User;
import com.shrimpking.service.UserService;
import com.shrimpking.service.impl.UserServiceImpl;
import com.shrimpking.utils.ApplicationContextUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Created by IntelliJ IDEA.
*
* @Author : Shrimpking
* @create 2023/10/2 21:42
* 自定义realm类
*/
public class CustomerRealm extends AuthorizingRealm
{
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection)
{
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException
{
//获取身份信息
String principal = authenticationToken.getPrincipal().toString();
User user = this.userService.getUserByUserName(principal);
//如果使用Autowired自动注入有问题时,请使用这个工具类,获取srping容器中的
//UserService userService = (UserService) ApplicationContextUtils.getBean("userServiceImpl");
//User user = userService.getUserByUserName(principal);
//判断
if(user != null) {
return new SimpleAuthenticationInfo(
principal,
user.getPassword(),
ByteSource.Util.bytes(user.getSalt().getBytes()),
this.getName()
);
}
return null;
}
}
utils
saltutils.java
package com.shrimpking.utils;
import java.util.Random;
/**
* Created by IntelliJ IDEA.
*
* @Author : Shrimpking
* @create 2023/10/3 10:59
* 生成随机加密盐
*/
public class SaltUtils
{
public static String getSalt(int n){
char[] chars = "abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ0123456789".toCharArray();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++)
{
char aChar = chars[new Random().nextInt(chars.length)];
sb.append(aChar);
}
return sb.toString();
}
}
applicationContextutils.java
package com.shrimpking.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* Created by IntelliJ IDEA.
*
* @Author : Shrimpking
* @create 2023/10/3 12:26
* 工具类,获取spring容器中的相应对象
*/
@Component
public class ApplicationContextUtils implements ApplicationContextAware
{
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
context = applicationContext;
}
/**
* 根据beanName获取spring容器中的类对象实例
* @param beanName
* @return
*/
public static Object getBean(String beanName){
return context.getBean(beanName);
}
}
jsp
login.jsp
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title></title>
</head>
<body>
<h1>登录管理</h1>
<form action="${pageContext.request.contextPath}/test/login" method="post">
账号:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
<a href="${pageContext.request.contextPath}/register.jsp">没有账号,请注册</a>
</body>
</html>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title></title>
</head>
<body>
<h1>系统主页</h1>
<a href="${pageContext.request.contextPath}/test/logout">退出系统</a>
<ul>
<li><a href="#">用户管理</a></li>
<li><a href="#">菜单管理</a></li>
<li><a href="#">其他管理</a></li>
</ul>
</body>
</html>
register.jsp
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title></title>
</head>
<body>
<h1>用户注册</h1>
<form action="${pageContext.request.contextPath}/user/register" method="post">
账号:<input type="text" name="username" required><br>
密码:<input type="password" name="password" required><br>
<input type="submit" value="立即注册">
</form>
</body>
</html>
test
package com.shrimpking;
import com.shrimpking.utils.SaltUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
/**
* Created by IntelliJ IDEA.
*
* @Author : Shrimpking
* @create 2023/10/3 11:09
*/
@SpringBootTest
public class SaltTest
{
@Test
public void test(){
String salt = SaltUtils.getSalt(4);
System.out.println(salt);
}
}