赞
踩
回顾:
【Spring Security】springboot + mybatis-plus + mysql 从数据库读取用户信息验证登录
本节实现 Spring Security 密码加密存储下的数据库用户验证登录,仅实现功能,不完善。
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring-security-learning</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>spring-security-02</module>
<module>spring-security-01</module>
<module>spring-security-03</module>
<module>spring-security-04</module>
<module>spring-security-05</module>
</modules>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.80</version>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
</dependencies>
</project>
#spring.datasource.name=mysql
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowMultiQueries=true
spring.datasource.username=xxxx
spring.datasource.password=xxxx
<?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="org.sample.dao.entity.UserPattern">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="org.sample.dao.entity.UserPattern">
<id column="id" property="id" />
<result column="username" property="username" />
<result column="passwd" property="passwd" />
<result column="enabled" property="enabled" />
<result column="account_non_expired" property="account_non_expired" />
<result column="account_non_locked" property="account_non_locked" />
<result column="credentials_non_expired" property="credentials_non_expired" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, username, passwd, enabled, account_non_expired, account_non_locked, credentials_non_expired
</sql>
</mapper>
package org.sample.config;
import com.alibaba.fastjson.JSON;
import org.sample.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.io.PrintWriter;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Autowired
private UserDetailsService userDetailsService;
/**
* PasswordEncoder 是密码加密接口,因为我们是循序渐进的,我这里先用无加密实例
* @return
*/
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 方法二
* 实现configure(AuthenticationManagerBuilder auth)配置方法
* 配置文件中的默认用户和默认密码注释掉了
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
/**
* 方法一
* auth.userDetailsService(userDetailsService())
*/
// @Bean
// public UserDetailsService userDetailsService(){
// return new UserDetailsService() {
// // username是用户登录时填的用户名
// public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// UserPattern user = new UserPattern();
// user.setUsername("mike");
// user.setPassword("123456");
// try {
// user = userService.loadByUserName(username);
// } catch (Exception e) {
// e.printStackTrace();
// }
//
// // 如果查不到用户名,这里可以抛出UsernameNotFoundException异常
// // 根据username查询权限,这里假设从任意位置查到权限是auth
// List<GrantedAuthority> authorities = new ArrayList<>();
// authorities.add(new SimpleGrantedAuthority("auth"));
// // User是系统自带的UserDetails实现类,4个状态其中一个为false就会抛异常
// return new User(user.getUsername(), user.getPassword(), true, true, true, true, authorities);
// }
// };
// }
/**
* 通过HttpSecurity 对象,获取到表单登录配置对象,修改对应的用户名和密码参数名称,即可完成自定义用户名和密码参数名称
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler((req,resp,authentication)->{
Object principal = authentication.getPrincipal();
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(JSON.toJSONString(principal));
out.flush();
out.close();
})
.failureHandler((req, resp, e) -> {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(JSON.toJSONString(e));
out.flush();
out.close();
})
.permitAll()
.and()
.exceptionHandling()
.authenticationEntryPoint((req, resp, authException) -> {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(JSON.toJSONString("尚未登录,请先登录"));
out.flush();
out.close();
}
)
.and()
.logout()
.logoutSuccessHandler((req,resp,authentication)->{
Object principal = authentication.getPrincipal();
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(JSON.toJSONString(principal));
out.flush();
out.close();
})
.and().csrf().disable()
;
}
public static void main(String[] args) {
BCryptPasswordEncoder b = new BCryptPasswordEncoder();
// $2a$10$hVPr4t.E21omWtyUdEsSEeMaEVkKp4oLHNjv/hlCrnBFI4WGf583a
String encode = b.encode("123456");
System.out.println(encode);
System.out.println(b.matches("123456", encode));
String encode2 = b.encode("123456");
System.out.println(encode2);
System.out.println(b.matches("123456", encode2));
}
}
Spring Security的配置类中的一个方法,用于配置HTTP安全和表单登录行为:
authorizeRequests()
:
使用 http.authorizeRequests()
方法开始配置授权规则。
antMatchers()
:
使用 antMatchers(“/admin/“).hasRole(“admin”) 配置对 /admin/ 开头的所有 URL 路径需要 “admin” 角色的用户才能访问。
使用 antMatchers(”/user/”).hasRole(“user”) 配置对 /user/ 开头的所有 URL 路径需要 “user” 角色的用户才能访问。
anyRequest().authenticated()
:
使用 anyRequest().authenticated() 配置所有其他未明确指定授权规则的请求都需要经过身份验证。
.and()
:
在多个配置之间使用 .and() 连接器进行组合。
formLogin()
:
使用 http.formLogin() 配置表单登录的相关参数。
successHandler()
:
自定义登录成功的处理器,当用户成功登录时,将执行此处理器。
在这个处理器中,获取认证后的主体信息(principal),将其转换为 JSON 格式,并写入到响应中。
failureHandler()
:
自定义登录失败的处理器,当用户登录失败时,将执行此处理器。
在这个处理器中,获取异常信息(Exception),将其转换为 JSON 格式,并写入到响应中。
permitAll()
:
设置表单登录相关的所有请求都不需要权限控制。
exceptionHandling()
:
使用 http.exceptionHandling() 配置异常处理相关的行为。
authenticationEntryPoint()
:
自定义未登录时的异常处理入口点,当用户未登录且尝试访问需要身份验证的资源时,将执行此处理器。在这个处理器中,向响应中写入一条提示信息,表示用户尚未登录。
logout()
:
使用 http.logout() 配置登出相关的行为。
logoutSuccessHandler()
:
自定义登出成功的处理器,当用户成功登出时,将执行此处理器。
在这个处理器中,获取认证后的主体信息(principal),将其转换为 JSON 格式,并写入到响应中。
.csrf().disable()
:
禁用跨站请求伪造(CSRF)防护。
总的来说,这段代码主要实现了以下功能:
配置了授权规则,包括对特定路径的访问权限控制。自定义了表单登录的成功和失败处理器,以及未登录和登出成功的异常处理入口点。禁用了 CSRF 防护。
package org.sample.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/sayHello")
public String sayHello(){
return "十年生死两茫茫,不思量,自难忘----苏轼,hello";
}
}
package org.sample.dao.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 测试用entity
*
* @author
* @since
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@TableName(value = "test.h_user")
public class UserPattern implements Serializable {
/**
* ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 用户名 userName
*/
@TableField(value = "username")
private String username;
/**
* 密码 password
*/
@TableField(value = "passwd")
private String password;
@TableField(value = "enabled")
private int enabled;
@TableField(value = "account_non_expired")
private int accountNonExpired;
@TableField(value = "account_non_locked")
private int accountNonLocked;
@TableField(value = "credentials_non_expired")
private int credentialsNonExpired;
}
package org.sample.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.sample.dao.entity.UserPattern;
/**
*
* @author
* @since
*/
public interface UserServiceMapper extends BaseMapper<UserPattern> {
}
package org.sample.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.sample.dao.entity.UserPattern;
/**
*
* @author
* @since
*/
public interface UserService extends IService<UserPattern> {
UserPattern loadByUserName(String username);
}
package org.sample.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.sample.dao.entity.UserPattern;
import org.sample.dao.mapper.UserServiceMapper;
import org.sample.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserServiceMapper, UserPattern> implements UserService, UserDetailsService {
@Autowired
private UserServiceMapper userServiceMapper;
/**
* 方法一
* auth.userDetailsService(userDetailsService())
* @param username
* @return
*/
@Override
public UserPattern loadByUserName(String username) {
List<UserPattern> userPatterns = userServiceMapper.selectList(Wrappers.lambdaQuery(UserPattern.class)
.likeLeft(UserPattern::getUsername, username)
.likeRight(UserPattern::getUsername, username)
);
UserPattern userPattern = userPatterns.get(0);
return userPattern;
}
/**
* 方法二
* auth.userDetailsService(userDetailsService)
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List<UserPattern> userPatterns = userServiceMapper.selectList(Wrappers.lambdaQuery(UserPattern.class)
.likeLeft(UserPattern::getUsername, username)
.likeRight(UserPattern::getUsername, username)
);
UserPattern userPattern = userPatterns.get(0);
// 如果查不到用户名,这里可以抛出UsernameNotFoundException异常
// 根据username查询权限,这里假设从任意位置查到权限是auth
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("auth"));
return new User(userPattern.getUsername(), userPattern.getPassword(), true, true, true, true, authorities);
}
}
package org.sample;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("org.sample.dao.mapper")
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
运行SecurityConfig.java
将一个加密密码复制,然后修改mysql数据库中的密码为加密密码:
运行MainApplication.java
打开postman:
调用一下接口,被未登录信息拦截:
我们登陆一下:
这里对用户来说密码是明文的,只是数据库中的密码为"123456"的加密密码:
显示登陆成功,之后再调用接口:
成功
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。