IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 根据Spring-Security安全框架搭建问答论坛系统(更新中.....) -> 正文阅读

[Java知识库]根据Spring-Security安全框架搭建问答论坛系统(更新中.....)

什么是Spring安全框架

Spring安全:Spring-Security

是Spring提供的安全管理框架,功能是提供一个安全可靠的登录功能,并且支持权限管理功能,而且自带判断当前用户是否登录的过滤器,如果用户没有登录会跳转到登录页面

为什么需要Spring-Security

使用Spring-Security框架能够使新手程序也能写出企业级别安全的登录功能

Spring-Security包含了权限管理的功能,能够方便的保存一个用户的各种权限,使用简单地方式判断这个用户是否包含这些权限,决定是否允许访问

能够帮助程序员提升编写登录和权限管理功能的开发效率

启动Spring-Security

启动Spring-Security非常的简单
只需要添加依赖即可

<!-- Spring Security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

加好这个依赖Spring-Security这个框架就会在项目中生效了

现在所有项目中的资源都会被Spring-Security保护

也就是说默认情况下,要想访问当前项目的任何资源,都需要先登录。

而登录方法是:
用户名:user
密码:启动服务时idea控制台出现的随机密码
随机密码
登录页面

访问控制器方法

打开创建好的UserController在其中添加一个方法

代码如下:

@RestController
//在类上编写@RequestMapping注解,表示当前控制器中的方法都需要以本注解
//添加的路径前缀来访问
@RequestMapping("/v1/users")
public class UserController {
    //编写控制器方法
    //结合类上面的注解,访问笨方法的最终路径是
    //localhost:8080/v1/users/get   
    @GetMapping("/get")
    public String get(){
        return "Hello html";
    }
}

重启服务,访问localhost:8080/v1/users/get
也是需要登录的,因为控制器的响应也属于网站资源,受到Spring-Security保护

在这里插入图片描述

密码加密

上面我们登录只能使用user这个用户,而且密码每次都要到控制台复制,比较麻烦

Spring-Security允许我们自定义的用户名和密码配置到application.properties中配置

#配置Spring-Security的自定义用户名和密码
spring.security.user.name=admin
spring.security.user.password=123456

但是这样配置的话,任何可以看到配置文件的人都可以登陆这个网站

所以我们需要学习密码加密,加密之后即使别人看到密码,也不能登录

我们可以使用市面上流星的安全加密算法:bcrypt

这个加密算法可以将任何数据进行加密保存,保证安全
在这里插入图片描述
在测试类中进行一个加密操作,代码如下:

@SpringBootTest
public class PasswordTest {
    //对Bcrypt加密对象实例化
    PasswordEncoder encoder = new BCryptPasswordEncoder();

    //执行加密测试
    @Test
    public void test(){
        //利用加密对象将str字符串加密为pwd
        String str = "123456";
        String pwd = encoder.encode(str);
        System.out.println(pwd);
    }

}

运行输出了一个加密结果后发现每次结果都不同,因为每次加密秘结果相同的话安全性较低,bcrypt加密算法采用了"随机校验"技术,让每次生成结果都不同.
加密结果在这里插入图片描述
加密完成下面进行验证的代码,bcrypt提供了验证的方法,可以判断一个字符串是否匹配一个加密结果

// 执行验证测试
@Test
public void match(){
// 下面的方法验证一个字符串是否匹配一个加密结果
// 返回boolean类型
boolean b=encoder.matches("123456",
"$2a$10$B5Ba4G77NuxAcRJ/iucipOaXjc/3uranz.lMW008IVxRdG
BATv8d2");
System.out.println("匹配结果:"+b);
}

最终目的是将加密结果配置在配置文件中application.properties文件修改为

#配置Spring-Security的自定义用户名和密码
spring.security.user.name=admin
spring.security.user.password={bcrypt}$2a$10$.6XmtLGrTwxO/JWWJCoc4OjoBQ7RG6cZ1WEHtYNbbYQWzVaqjTj2i

Spring-Security的权限管理功能

我们最终的登录是要支持现有数据库中所有user的数据

现在只能支持配置文件中的用户

如果要实现数据库登录,首先要有机会在java代码中设置用户名密码

创建一个security包,包中创建SecurityConfig,代码如下:

@Configuration
//启用spring-security提供的权限管理功能
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //当前类继承WebSecurityConfigurerAdapter
    //能够重写这个父类中的方法,这个父类中的方法都是用于设置权限管理的

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("tom")
                .password("{bcrpt}$2a$10$.6XmtLGrTwxO/JWWJCoc4OjoBQ7RG6cZ1WEHtYNbbYQWzVaqjTj2i")
                .authorities("test");
        //上面代码的含义是在Spring-Security框架中定义了一个用户
        //用户名是tom,密码是123456
        //具有"test"这个资格可以使当前用户具有访问test资格资源的访问权限
        //当我们设置这个用户之后,配置文件中设置的用户admin就失效了
    }
}

控制器方法可以设定当前方法需要什么特殊权限才能访问,如果不设置默认情况下登录就可以访问

修改UserController代码如下:

@RestController
// 在类上编写下面注解,表示当前控制器中的方法都需要
// 以本注解添加的路径前缀来访问
@RequestMapping("/v1/users")
public class UserController {
// 编写控制器方法
// 结合类上面的注解,访问本方法的最终路径是
// localhost:8080/v1/users/get
@GetMapping("/get")
public String get(){
return "Hello html";
}
// 上面的方法没有设置特殊权限登录就可以访问
// 下面方法设置特殊权限,必须有匹配的资格才能访问
@GetMapping("/list")
// 当前这个方法必须是拥有test资格的用户才能访问
@PreAuthorize("hasAuthority('run')")
public String list(){
return "get list";
}
}


实现数据库中的用户登录

通过之前的部分,现在知道在Java代码中,要想让用户登录至少要提供用户名,密码,和当前用户权限

数据库用户表中没有直接提供当前用户的权限,那么我们就要根据对当前用户的id查询当前用户的权限
在这里插入图片描述
这个查询可能涉及上面的5张表

因为用户对角色和角色对权限都是多对多

今后面试时如果问到权限数据的实现方式,需要回答上面的5张表

我们需要编写一个根据用户id查询所有权限的5表联查的sql语句

SELECT p.id , p.name
FROM user u
LEFT JOIN user_role ur ON u.id=ur.user_id
LEFT JOIN role r ON r.id=ur.role_id
LEFT JOIN role_permission rp ON r.id=rp.role_id
LEFT JOIN permission p ON p.id=rp.permission_id
WHERE u.id=11

我们需要在数据访问层编写这个方法,在登录业务中需要时调用

打开UserMapper编写代码如下


@Repository
public interface UserMapper extends BaseMapper<User> {

    //根据用户id 查询用户所有权限的方法
    @Select("SELECT p.id , p.name\n" +
            "FROM user u\n" +
            "LEFT JOIN user_role ur ON u.id=ur.user_id\n" +
            "LEFT JOIN role r ON r.id=ur.role_id\n" +
            "LEFT JOIN role_permission rp ON r.id=rp.role_id\n" +
            "LEFT JOIN permission p ON p.id=rp.permission_id\n" +
            "WHERE u.id=#{id}")
    List<Permission> findUserPermissionsById(Integer id);

    //根据用户名查询用户对象
    @Select("select * from user where username=#{username}")
    User findUserByUsername(String username);
}

有了用户名,密码和用户权限等信息

下面就可以按照Spring-Security规定方式进行登录代码的编写了

我们需要自己编写一个类,这个类实现Spring-Security提供的恶一个接口UserDetailsService,而这个接口中需要实现一个方法,这个方法的功能是根据用户输入在登录框中的用户名进行用户信息(用户名密码权限)的查询,返回值必须是UserDetails,我们需要将这个类型对象实例化后赋值最后返回以完成登录

在service.impl包中新建一个类UserDetailsServiceImpl

代码如下


@Component
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    //当前类需要保存到Spring容器@Component不能少
    //需要基于Spring-Security设计的方法进行登录,实现UserDetailsService接口
    //下面方法是接口提供的,我们来实现
    //方法的参数是用户在登录表单编写的用户名
    //方法的返回值是UserDetails类型对象包含登录需要的用户名密码权限等
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //1.根据用户名查询用户对象
        User user= userMapper.findUserByUsername(username);
        //2.判断是否能够查询到用户,没有该用户表示用户名不存在
        if (user==null){
            return null;
        }
        //3.根据用户id查询用户的所有权限
        List<Permission> permissions=
                userMapper.findUserPermissionsById(user.getId());
        //4.将权限的集合转换为String类型数组进行赋值
        String[] auth = new String[permissions.size()];
        int i =0;
        for (Permission p : permissions){
            auth[i]=p.getName();
            i++;
        }
        //5.构建UserDetails对象
        UserDetails details =
                org.springframework.security.core.userdetails.User.builder()
                .username(user.getUsername())
                .password(user.getPassword())
                .authorities(auth).accountLocked(user.getLocked()==1)//设置当前用户是否锁定
                .disabled(user.getEnabled()==0)//设置当前用户是否可用 false表示可用
                .build();
        //6.返回
        return details;
    }
}

上面的代码是Spring-Security要求我们编写的完成登录功能的代码

我们要想登录成功,还要将这个类型对象和Spring-Security建立关系

回到security包中的SecurityConfig类

将我们之前编写的configure方法修改为

// 表示当前配置类是配置Spring框架的
@Configuration
// 启动Spring-Security提供的权限管理功能
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends
                        WebSecurityConfigurerAdapter {
    //当前类继承WebSecurityConfigurerAdapter
    // 能够重写这个父类中的方法,这个父类中的方法都是用于设置权限管理的

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);

    }
}

设置放行页面

当今流行的网站都是有些页面允许不登录就能访问

而我们现在的Spring-Security下所有资源都需要登录才能访问

我们如果想放行一些页面需要配置下面代码

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests() // 设置网站的访问及放行规则
            // 下面的方法开始指定路径
            .antMatchers(
                    "/index_student.html",
                    "/css/*",
                    "/js/*",
                    "/img/**",
                    "/bower_components/**")
            .permitAll() // 上面的路径是全部允许的(不需要登录就能访问)
            .anyRequest() // 除上面之外的其他路径
            .authenticated() // 需要登录才能访问
            .and()  //上面的配置完成了,开始配置下面的
            .formLogin(); // 使用表单进行登录
}

自定义登录页面

Spring-Security提供的默认登录页面不能体现当前网站的特征,也不能编写其他功能或连接,很受限制,我们希望能够使用自定义的login.html页面进行登录操作

也是需要进行对应的配置

SecurityConfig继续配置

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable() //禁用防跨域攻击功能
            .authorizeRequests() // 设置网站的访问及放行规则
            // 下面的方法开始指定路径
            .antMatchers(
                    "/index_student.html",
                    "/css/*",
                    "/js/*",
                    "/img/**",
                    "/bower_components/**",
                    "/login.html")
            .permitAll() // 上面的路径是全部允许的(不需要登录就能访问)
            .anyRequest() // 除上面之外的其他路径
            .authenticated() // 需要登录才能访问
            .and()  //上面的配置完成了,开始配置下面的
            .formLogin() // 使用表单进行登录
            .loginPage("/login.html") //配置登录时显示的页面
            .loginProcessingUrl("/login") //配置处理登录的路径
            .failureUrl("/login.html?error")// 登录失败跳转的页面
            .defaultSuccessUrl("/index_student.html")// 登录成功跳转的页面
            .and()
            .logout()
            .logoutUrl("/logout") // 配置登出的链接
            .logoutSuccessUrl("/login.html?logout");// 登出后跳转回登录页

}

注册功能流程分析

注册业务流程

  1. 学生填写注册表单信息
  2. 提交注册信息到控制器
  3. 控制器接收到信息调佣业务逻辑层方法
  4. 业务逻辑层中判断邀请码,手机号并对密码加密后进行数据库新增
  5. mapper层执行新增方法,返回到业务逻辑层
  6. 业务逻辑层将注册结果返回给控制鞥
  7. 控制层将最终信息显示在页面上

注册业务准备

首设置注册页面和控制器路径的放行

打开SecurityConfig配置类,进行放行配置

http.csrf().disable() //禁用防跨域攻击功能
                .authorizeRequests() // 设置网站的访问及放行规则
                // 下面的方法开始指定路径
                .antMatchers(
                        "/index_student.html",
                        "/css/*",
                        "/js/*",
                        "/img/**",
                        "/bower_components/**",
                        "/login.html",
                        "/register.html",
                        "/register")
                .permitAll() // 上面的路径是全部允许的(不需要登录就能访问)
                .anyRequest() // 除上面之外的其他路径
                .authenticated() // 需要登录才能访问
                .and()  //上面的配置完成了,开始配置下面的
                .formLogin() // 使用表单进行登录
                .loginPage("/login.html") //配置登录时显示的页面
                .loginProcessingUrl("/login") //配置处理登录的路径
                .failureUrl("/login.html?error")// 登录失败跳转的页面
                .defaultSuccessUrl("/index_student.html")// 登录成功跳转的页面
                .and()
                .logout()
                .logoutUrl("/logout") // 配置登出的链接
                .logoutSuccessUrl("/login.html?logout");// 登出后跳转回登录页

根据表单参数创建vo类

@Data
public class RegisterVo implements Serializable {
private String inviteCode; //邀请码
private String phone; //手机号\用户名
private String nickname; //昵称
private String password; //密码
private String confirm; //确认密码
}

还需要自定义异常类

在我们编写的业务发生异常不能继续运行时,使用抛出异常的方式反馈
错误信息

我们定义一个自定义异常类,ServiceException

来表示业务逻辑运行过程中发生的各种不能继续运行程序的异常

例如:邀请码不正确\手机号已经被注册

新建一个包exception,新建类代码如下

public class ServiceException extends RuntimeException{
private int code = 500;
public ServiceException() { }
public ServiceException(String message) {
super(message);
}
public ServiceException(String message, Throwable
cause) {
super(message, cause);
}
public ServiceException(Throwable cause) {
super(cause);
}
public ServiceException(String message, Throwable
cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression,
writableStackTrace);
}
public ServiceException(int code) {
this.code = code;
}
public ServiceException(String message, int code) {
super(message);
this.code = code;
}
public ServiceException(String message, Throwable
cause,
int code) {
super(message, cause);
this.code = code;
}
public ServiceException(Throwable cause, int code)
{
super(cause);
this.code = code;
}
public ServiceException(String message, Throwable
cause,
boolean enableSuppression,
boolean writableStackTrace, int code) {
super(message, cause, enableSuppression,
writableStackTrace);
this.code = code;
}
public int getCode() {
return code;
}
}

还可以设置简单条件,适合偶尔一次查询数据库使用
我们在测试类中编写一个测试,按邀请码查询班级信息
代码如下

// 根据邀请码查询班级信息
// 如果写sql语句:
// select * from classroom where invite_code='JSD2001-
706246'
// 如果使用QueryWrapper进行查询 代码如下
@Autowired
ClassroomMapper classroomMapper;
@Test
public void query(){
// 我们实例化一个QueryWrapper的对象
// 这个对象其实就是代表查询的条件,泛型是实体类的类型
QueryWrapper<Classroom> query=new QueryWrapper<>();
// 设置查询条件 query.eq([列名],[值])
query.eq("invite_code","JSD2001-70624");
// 按QueryWrapper对象设置好的条件进行查询的操作
// selectOne方法只支持最多返回1行数据,否则报错,返回值是实
体类型
Classroom
classroom=classroomMapper.selectOne(query);
System.out.println(classroom);
}

业务逻辑层概述

Vrd项目中完成一次请求响应流程一般会由两个部分组成
请求->控制器(controller)->数据访问层(mapper)
上面的执行流程只能处理相对简单的业务逻辑层
如果遇到企业中的相对复杂的业务逻辑就不能很好的处理了
每个类都应该有自己的职责
// 根据邀请码查询班级信息
// 如果写sql语句:
// select * from classroom where invite_code=‘JSD2001-
706246’
// 如果使用QueryWrapper进行查询 代码如下
@Autowired
ClassroomMapper classroomMapper;
@Test
public void query(){
// 我们实例化一个QueryWrapper的对象
// 这个对象其实就是代表查询的条件,泛型是实体类的类型
QueryWrapper query=new QueryWrapper<>();
// 设置查询条件 query.eq([列名],[值])
query.eq(“invite_code”,“JSD2001-70624”);
// 按QueryWrapper对象设置好的条件进行查询的操作
// selectOne方法只支持最多返回1行数据,否则报错,返回值是实
体类型
Classroom
classroom=classroomMapper.selectOne(query);
System.out.println(classroom);
}
controller:职责就是接收前端页面的信息和将结果响应给页面,其他的事
情尽量不管
mapper:完成对数据库的增删改查操作,其他的操作也不管
如果出现了既不属于controller的也不属于mapper职责的工作,就需要
写在业务逻辑层中
service(业务逻辑层):职责就是将前端发送来的信息经过处理再调用数据
访问层的功能,例如我们接收了用户输入的邀请码但是需要判断是否正

企业标准中,service又由两个部分组成
service和service.impl
service中保存业务逻辑层接口:一般命名为IXXXService(开头的I表示
Interface)
service.impl中保存业务逻辑层实现类:一般命名为
XXXServiceImpl(Impl表示实现的缩写)
之所以采用接口配实现类的形式,是为了解耦
所以在需要业务逻辑层代码时,我们都声明接口类型
再今后我们开发程序的模型中,控制层,业务逻辑层,数据访问层这三层结
构如下
在这里插入图片描述

开发注册业务逻辑层代码

实际开发中,应该先完成数据访问层的编写,但是当前业务中,所有数据库
操作都是基本增删改查,已经由MybatisPlus提供了,所以Mapper层不需
要编写代码
先编写业务逻辑层接口
IUserService添加方法如下

public interface IUserService extends IService<User> {
// 在接口中如果想转到实现类快捷键Ctrl+Alt+B
void registerStudent(RegisterVo registerVo);
}

UserServiceImpl实现类代码如下

@Service
public class UserServiceImpl extends
ServiceImpl<UserMapper, User> implements IUserService {
//注入注册需要的各种依赖
@Autowired
private UserMapper userMapper;
@Autowired
private ClassroomMapper classroomMapper;
@Autowired
private UserRoleMapper userRoleMapper;
@Override
public void registerStudent(RegisterVo registerVo)
{
// 1.根据用户输入的邀请码获得班级信息
QueryWrapper<Classroom> query=new
QueryWrapper<>();
query.eq("invite_code",registerVo.getInviteCode());
Classroom
classroom=classroomMapper.selectOne(query);
// 2.判断班级信息是否存在,不存在直接抛异常
if(classroom==null){
throw new ServiceException("邀请码错误!");
}
// 3.根据用户输入的手机号,获得用户信息
User user=userMapper.findUserByUsername(
registerVo.getPhone());
// 4.如果能够获得用户信息,表示当前手机号已经被注册,抛出
异常
if(user!=null){
throw new ServiceException("手机号已经被注
册!");
}
// 5.对用户输入的密码进行加密
PasswordEncoder encoder=new
BCryptPasswordEncoder();
String pwd="
{bcrypt}"+encoder.encode(registerVo.getPassword());
// 6.实例化用户对象,为各个属性赋值,收集用户信息
User u=new User()
.setUsername(registerVo.getPhone())
.setNickname(registerVo.getNickname())
.setPassword(pwd)
.setClassroomId(classroom.getId())
.setCreatetime(LocalDateTime.now())
.setEnabled(1)
.setLocked(0)
.setType(0);
// 7.执行新增用户对象的操作
int num=userMapper.insert(u);
if(num!=1){
throw new ServiceException("数据库忙");
}
// 8.执行新增用户角色关系表的操作
UserRole userRole=new UserRole()
.setUserId(u.getId())
.setRoleId(2);
num=userRoleMapper.insert(userRole);
if(num!=1){
throw new ServiceException("数据库忙");
}
}
}




推荐大家编写完比价复杂的业务逻辑代码时进行测试
代码如下

@Autowired
IUserService userService;
@Test
public void add(){
RegisterVo registerVo=new RegisterVo();
registerVo.setPhone("13033012345");
registerVo.setNickname("大龙");
registerVo.setInviteCode("JSD2001-706246");
registerVo.setPassword("123456");
userService.registerStudent(registerVo);
System.out.println("ok");
}

开发控制层代码

我们先来编写控制层接收表单信息的代码

创建SystemController类

编写代码如下

@RestController
// lombok提供的一个记录日志用的注解
// 一旦在类上添加@Slf4j,这个类的方法中就可以使用log对象记录日志
@Slf4j
public class SystemController {
@Autowired
private IUserService userService;
@PostMapping("/register")
public String register(RegisterVo registerVo){
//利用日志对象,将接收到的信息输出到控制台
log.debug("接收到用户信息:{}",registerVo);
try {
userService.registerStudent(registerVo);
return "ok";
}catch (ServiceException e){
log.error("注册失败",e);
return e.getMessage();
}
}
}

重启服务测试

注册成功表示代码正确,检查数据库user表和user_role表示的信息

修改为异步测试

gitee同步更新中
将一个同步的注册修改为异步注册需要如下修改

  1. 页面中需要的支持(vue,axios等)
<script
src="https://unpkg.com/axios/dist/axios.min.js">
</script>
  1. 编写并引用js代码
</body>
<script src="js/utils.js"></script>
<script src="js/register.js"></script>
</html>
  1. 页面的vue绑定(v-model…)

34行开始

<!--
@submit表示在表单提交时运行指定方法
.prevent阻止表单原有的提交动作
-->
<form action="/register" method="post"
@submit.prevent="register">
<div class="form-group has-icon">
<input type="text" name="inviteCode" class="formcontrol" placeholder="请输入邀请码"
required="required" v-model="inviteCode">
<span class="fa fa-barcode form-control-icon">
</span>
</div>
<div class="form-group has-icon">
<input type="tel" name="phone" class="form-control"
placeholder="请输入手机号"
pattern="^\d{11}$" required="required"
v-model="phone">
<span class=" fa fa-phone form-control-icon">
</span>
</div>
<div class="form-group has-icon">
<input type="text" name="nickname" class="formcontrol" placeholder="请设置昵称,字数为2-20之间"
pattern="^.{2,20}$" required="required"
v-model="nickname">
<span class="fa fa-user form-control-icon"></span>
</div>
<div class="form-group has-icon">
<input type="password" name="password" class="formcontrol" placeholder="设置密码6-20个字母、数字、下划线"
required="required" pattern="^\w{6,20}$"
v-model="password">
<span class="fa fa-lock form-control-icon"></span>
</div>
<div class="form-group has-icon">
<input type="password" name="confirm" class="formcontrol" placeholder="请再次输入密码"
required="required"
v-model="confirm">
<span class="fa fa-lock form-control-icon"></span>
</div>
<button type="submit" class="btn btn-primary btnblock btn-flat" >注册</button>
</form>

我们需要修改一下注册页面显示错误信息的区域
30行附近

<!--
:class="" 是利用vue提供的逻辑判断决定是否向样式中添加d-block
hasError为true d-block生效,错误信息显示
hasError为false d-block无效,错误信息隐藏
-->
<div id="error" class="alert alert-danger"
style="display: none"
:class="{'d-block':hasError}">
<i class="fa fa-exclamation-triangle"></i>
<span v-text="message" >邀请码错误!</span>
</div>

register.js代码 if判断修改一下

.then(function(r) {
console.log("|"+r.status+"|"+OK+"|");
if(r.data=="ok"){
console.log("注册成功");
console.log(r.data);
app.hasError = false;
location.href = '/login.html?register';
}else{
console.log(r.data);
app.hasError = true;
app.message = r.data;
}
});

Spring验证框架

表单验证基本概念

我们页面中通过编写html5diamante可以实现表单验证的逻辑

一般情况下,非空正则表达式的验证都是被支持的

正常编写表单验证之后,可以降低服务器的压力,提高服务器的运行性能

表单眼中正式非常好的验证方案,但是也有缺点:它只能防止通过表单进行注册的用户提交错误信息

如果有人恶意绕开浏览器直接将请求信息发送给服务器。服务器就会处理错误信息,严重情况下服务器就会瘫痪,我们需要服务端进行验证

有了服务端验证,才能防止绕开浏览器的数据不经过验证就进入数据库

这样的验证我们自己写是比较繁琐的,我们需要框架帮助我们简化

什么是Spring验证框架

Spring验证框架:Spring Validation
它能实现简单方便的服务器端验证并且能够接收验证结果

Spring Validation的使用

要想使用先添加依赖

<!-- 验证框架 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-startervalidation</artifactId>
</dependency>

添加完成依赖之后,刷新maven,到需要验证的类中边写正则表达式,也支持非空验证和正则表达式验证等其他验证方式

@Data
public class RegisterVo implements Serializable {
//message就是当本属性为空时,输出的错误信息
@NotBlank(message = "邀请码不能为空")
private String inviteCode; //邀请码
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^1\\d{10}$",message = "手机号格式
不正确")
private String phone; //手机号\用户名
@NotBlank(message = "昵称不能为空")
@Pattern(regexp = "^.{2,20}$",message = "昵称是2~20
位字符")
private String nickname; //昵称
@NotBlank(message = "密码不能为空")
@Pattern(regexp = "^\\w{6,20}$",message = "密码是
6~20位字符")
private String password; //密码
@NotBlank(message = "确认密码不能为空")
private String confirm; //确认密码
}

控制器启动验证

@PostMapping("/register")
public String register(
// RegisterVo参数前添加@Validated表示开启服务器端验
证功能
// 当控制器方法运行之前,SpringValidation框架会按
RegisterVo
// 中编写的规则进行验证
@Validated RegisterVo registerVo,
// 这个参数必须紧随RegisterVo之后
// result中包含RegisterVo的验证结果
BindingResult result){
//利用日志对象,将接收到的信息输出到控制台
log.debug("接收到用户信息:{}",registerVo);
if(result.hasErrors()){
String msg=result.getFieldError().
getDefaultMessage();
// 返回错误信息
return msg;
}
try {
userService.registerStudent(registerVo);
return "ok";
}catch (ServiceException e){
log.error("注册失败",e);
return e.getMessage();
}
}

观察效果,我们还是使用浏览器来测试
所以可以暂时删除一些html5的表单验证

随笔

QueryWrapper方法含义:

eq(): equals-等于
gt(): grate than 大于
lt(): less than 小于
ge(): grate
equals 大于等于
le(): 小于等于
ne(): not equals 不等于

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-11-01 11:32:51  更:2021-11-01 11:33:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年10日历 -2024/10/30 13:25:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码