springboot集成security(认证)
Posted 364.99°
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot集成security(认证)相关的知识,希望对你有一定的参考价值。
目录
1. 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
加入 spring-boot-starter-security 依赖之后,不需要任何配置,启动项目,就会出现一个登录界面:
-
任何访问请求都会被拦截到 /login,并且要求认证后才能访问,请求方式是 Get
-
默认 username 是 user,每次启动都会在控制台输出一个 128Byte 的 password
-
可通过配置文件修改默认的 username 和 password (静态用户,适用于内部网络认证)
spring: # default login url: /login security: user: name: dev # default size: 128 Byte password: admin
2. 自定义登录逻辑
1. 数据库查询
表、实体类:
@Data
public class User
private Integer id;
private String name;
private String password;
配置文件:
# application name
spring:
# mysql
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/spbt?serverTimezone=Asia/Shanghai
username: root
password: admin
# web service port
server:
port: 8088
mybatis-plus:
type-aliases-package: com.chenjy.security_demo.dto
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper:
<?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.chenjy.security_demo.mapper.UserMapper">
<select id="getUserByName" parameterType="string" resultType="user">
select
id,
name,
password
from
user
<where>
<if test="name != null and name != ''">
name = #name
</if>
</where>
</select>
</mapper>
@Mapper
public interface UserMapper
User getUserByName(String name);
List<User> getUserByName();
启动类加上 @MapperScan 注解
service:
public interface UserService
User getUserByName(String name);
List<User> getUserByName();
@Service
public class UserServiceImpl implements UserService
@Resource
private UserMapper userMapper;
@Override
public User getUserByName(String name)
return userMapper.getUserByName(name);
@Override
public List<User> getUserByName()
return userMapper.getUserByName();
2. security认证
要自定义 security 认证逻辑,就需要定义一个服务对象来实现 UserDetailsService 接口,并重写 loadUserByUsername 方法。
1. loadUserByUsername
loadUserByUsername
- security 唯一的认证方法
- 查询用户失败,会抛出 UsernameNotFoundException
@Component
public class UserDetailsServiceImpl implements UserDetailsService
@Resource
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
com.chenjy.security_demo.dto.User user = userService.getUserByName(username);
if (user == null)
throw new UsernameNotFoundException("用户名错误");
// 匹配用户密码
// org.springframework.security.core.userdetails.User
User res = new User(username, user.getPassword(), AuthorityUtils.createAuthorityList());
return res;
-
org.springframework.security.core.userdetails.User
-
loadUserByUsername 需要返回一个 UserDetails 的实现类,可以直接使用 User,其有两个构造器:
public User(String username, String password, Collection<? extends GrantedAuthority> authorities) public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities)
- username 用户名
- password 用户密码
- authorities 权限集合
-
security 内部会自动进行密码匹配
这时候直接启动项目,会报错:
*java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"*
这是因为 security 5.X 需要提供一个PasswordEncorder的实例 。
2. PasswordEncorder(不加密)
security 内部进行密码匹配的时候,一定要进行加密和解密处理。要求 IOC 容器中,必须要存在一个 PasswordEncorder 对象,提供加密和解密逻辑。
- 可以直接客户端明文,数据库解密来匹配验证
- 也可以客户端加密,数据库直接密文来匹配验证
因为我的数据库密码并未进行加密,所以这里,我们就直接返回字符串进行明文比对就行了。
@Component
public class MyPasswordEncoder implements PasswordEncoder
/**
* @Description 收集页面的密码
* @param rawPassword
* @return String
*/
@Override
public String encode(CharSequence rawPassword)
return rawPassword.toString();
/**
* @Description 匹配逻辑
* @param rawPassword 明文,页面收集的密码
* @param encodedPassword 密文,存储在数据源中的密码
* @return boolean
*/
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword)
return rawPassword.equals(encodedPassword);
encode 对页面收集到的密码进行加密,然后将加密好的密文交给 matches,与数据库密码进行比对。
3. MD5加密数据库密码
自定义加密工具类
public class PwdEncode
/**
* @Description 收集
* @param pwd
* @return String
*/
public static String encode(String pwd)
MessageDigest md5 = null;
try
md5 = MessageDigest.getInstance("MD5");
catch (Exception e)
System.out.println(e.toString());
e.printStackTrace();
return "";
char[] charArray = pwd.toCharArray();
byte[] byteArray = new byte[charArray.length];
for (int i = 0; i < charArray.length; i++)
byteArray[i] = (byte) charArray[i];
byte[] md5Bytes = md5.digest(byteArray);
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++)
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16)
hexValue.append("0");
hexValue.append(Integer.toHexString(val));
return hexValue.toString();
/**
* @Description 解密
* 一次加密之后,需要两次解密
* @param pwd
* @return String
*/
public static String decrypt(String pwd)
char[] a = pwd.toCharArray();
for (int i = 0; i < a.length; i++)
a[i] = (char) (a[i] ^ 't');
String s = new String(a);
return s;
public static void main(String[] args)
String s = "123456";
System.out.println("原始:" + s);
System.out.println("MD5后:" + encode(s));
System.out.println("加密的:" + decrypt(s));
System.out.println("解密的:" + decrypt(decrypt(s)));
然后修改数据库密码。
4. PasswordEncorder(加密)
@Component
public class MyPasswordEncoder implements PasswordEncoder
/**
* @Description 收集页面的密码
* @param rawPassword
* @return String
*/
@Override
public String encode(CharSequence rawPassword)
return rawPassword.toString();
/**
* @Description 匹配逻辑
* @param rawPassword 明文,页面收集的密码
* @param encodedPassword 密文,存储在数据源中的密码
* @return boolean
*/
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword)
return PwdEncode.encode(rawPassword.toString()).equals(encodedPassword);
注意: MD5解密很简单——https://www.cmd5.com/,所以不推荐使用MD5加密。
5. BCryptPasswordEncoder
鉴于MD5加密的不安全性,所以建议使用 security 自带的加密工具类 —— BCryptPasswordEncoder 。
-
同一明文,加密两次,输出不同
public static void main(String[] args) String s = "123456"; BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();; System.out.println("原始: " + s); System.out.println("加密1: " + encoder.encode(s)); System.out.println("加密1: " + encoder.encode(s));
-
可使用 matches 验证明文与密文是否相同:
System.out.println(encoder.matches(s, encoder.encode(s)));
BCryptPasswordEncoder使用bcrypt算法对密码进行加密,同时会为密码加上“盐”,开发者不需要自己加“盐”,即使相同的明文字段生成的加密字符串也不同。匹配时,从密文中取出“盐”,用该盐值加密明文和最终密文作对比。
BCryptPasswordEncoder的默认强度为10,开发者可以根据自己服务器的速度进行调整,以确保密码验证的时间约为1秒(官方建议)
BCryptPasswordEncoder encoder_pro = new BCryptPasswordEncoder(15);
System.out.println("加密2: " + encoder_pro.encode(s));
注册 BCryptPasswordEncoder:
-
取消MyPasswordEncoder的 @Component 注解
-
新建一个配置类,注册 BCryptPasswordEncoder
@Configuration public class SecurityConf @Bean public BCryptPasswordEncoder passwordEncoder() return new BCryptPasswordEncoder();
6. 认证流程(图)
3. 自定义登录界面
1. 界面
依赖:
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
resources/templates/login.html:
<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<div class="main">
<div class="title">
<span>密码登录</span>
</div>
<div class="title-msg">
<span>请输入登录账户和密码</span>
</div>
<form class="login-form" method="post" novalidate th:action="@/myLogin">
<!--输入框-->
<div class="input-content">
<!--autoFocus-->
<div>
<input type="text"
autocomplete="off"
placeholder="用户名"
name="name"
required/>
</div>
<div style="margin-top: 16px">
<input type="password"
autocomplete="off"
placeholder="登录密码"
name="password"
required
maxlength="32"/>
</div>
</div>
<!--登入按钮-->
<div style="text-align: center">
<button type="submit" class="enter-btn" >登录</button>
</div>
</form>
</div>
</body>
<style>
body
background: #426258;
*
padding: 0;
margin: 0;
.main
padding-left: 25px以上是关于springboot集成security(认证)的主要内容,如果未能解决你的问题,请参考以下文章
springboot集成spring security实现restful风格的登录认证 附代码