0
点赞
收藏
分享

微信扫一扫

从零开始搭建springboot-security

无聊到学习 2022-04-24 阅读 45
spring

创建springboot项目

  • 创建父工程security-oauth2-demo2
  • 创建子工程oauth2-service
  • 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<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>
  • 配置文件application.yml
server:
port: 8081
servlet:
context-path: /demo2
  • 创建controller
@RestController
public class UserController {

@RequestMapping("/hello")
public String hello(){
return "Hello world!";
}
}
  • 项目结构

  •  启动访问

整合security

  •  引入依赖

        <!--数据库依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>5.1.47</scope>
</dependency>
<!--security依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
  • 创建数据库表
CREATE TABLE `security2`.`Untitled`  (
`id` int(0) NOT NULL AUTO_INCREMENT COMMENT '主键',
`username` varchar(100) COMMENT '账号',
`password` varchar(255) COMMENT '密码',
`realname` varchar(100) CHARACTER SET utf8 COMMENT '名称',
`auth` varchar(255) COMMENT '权限',
PRIMARY KEY (`id`)
);
INSERT INTO `t_user` VALUES (1, 'zhangsan', '123', '张三', 'p1');
INSERT INTO `t_user` VALUES (2, 'lisi', '202CB962AC59075B964B07152D234B70', '李四', 'p2');
  • application.yml添加数据库配置
server:
port: 8081
servlet:
context-path: /demo2
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
validation-query: SELECT 1
username: root
password: root
url: jdbc:mysql://localhost:3306/security2?useUnicode=true&characterEncoding=utf-8
  • 创建dao查询数据库
package com.security.oauth2.oauth2service.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.Map;

@Repository
public class UserDao {

@Autowired
private JdbcTemplate jdbcTemplate;

/**
* 根据账号查询
* @return
*/

public Map<String,Object> getUserByUsername(String userName){
String sql = "select * from t_user where username=?";
Map<String, Object> user = jdbcTemplate.queryForMap(sql, new Object[]{userName});
return user;
}

}
  • 创建MyUserDetailsService实现UserDetailsService中的登录验证方法
package com.security.oauth2.oauth2service.config.security;

import com.security.oauth2.oauth2service.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
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.Component;

import java.util.Map;

/**
* 重写登录规则,并加入容器
*/

@Component
public class MyUserDetailsService implements UserDetailsService {

@Autowired
private UserDao userDao;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("username:"+username);
//从数据库查询
Map<String,Object> dbUser = userDao.getUserByUsername(username);
if (dbUser == null){
return null;
}
//根据用户名查询用户权限
String[] premiss = dbUser.get("auth").toString().split(",");
UserDetails build = User
//账号
.withUsername(dbUser.get("username").toString())
//密码
.password(dbUser.get("password").toString())
//权限
.authorities(premiss).build();
return build;
}
}
  • 创建WebSecurityConfig文件继承WebSecurityConfigurerAdapter重写核心配置方法,并配置密码验证规则为不加密
package com.security.oauth2.oauth2service.config.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration //标志为一个配置文件
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Bean
public PasswordEncoder passwordEncoder(){
//密码规则为原始数据,不加密
return NoOpPasswordEncoder.getInstance();
}
/**
* 认证策略,核心配置,具体的权限控制规则配置
*/

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//设置r1路径的访问权限是 p1
.antMatchers("/r/r1").hasAuthority("p1")
//设置r2路径的访问权限是 p2
.antMatchers("/r/r2").hasAuthority("p2")
//除了 /r/**.其他请求可以访问
.anyRequest().permitAll()
.and()
//开启表单登录,如果检测导没有登录会跳转到登录页
.formLogin();
}

}
  • controller添加访问路径
package com.security.oauth2.oauth2service.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

@RequestMapping("/hello")
public String hello(){
return "Hello world!";
}

@RequestMapping("/r/r1")
public String r1(){
return "访问资源1!";
}
@RequestMapping("/r/r2")
public String r2(){
return "访问资源2!";
}
}
  • 目录结构

 

  • 启动访问http://localhost:8081/demo2/r/r1会跳转到登录

输入账号zhangsan密码123 

点击登录

访问r2

 

启用权限注解

  • 修改WebSecurityConfig配置文件

1、添加注解@EnableGlobalMethodSecurity(prePostEnabled = true),意思是开启前置权限注解@PrePostEnabled的使用

2、取消r1和r2的核心配置

package com.security.oauth2.oauth2service.config.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration //标志为一个配置文件
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Bean
public PasswordEncoder passwordEncoder(){
//密码规则为原始数据,不加密
return NoOpPasswordEncoder.getInstance();
}
/**
* 认证策略,核心配置,具体的权限控制规则配置
*/

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//设置r1路径的访问权限是 p1
// .antMatchers("/r/r1").hasAuthority("p1")
// //设置r2路径的访问权限是 p2
// .antMatchers("/r/r2").hasAuthority("p2")
//请求可以访问
.anyRequest().permitAll()
.and()
//开启表单登录,如果检测导没有登录会跳转到登录页
.formLogin();
}

}
  • 修改controller方法,添加@PrePostEnabled注解
package com.security.oauth2.oauth2service.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

@RequestMapping("/hello")
public String hello(){
return "Hello world!";
}

@RequestMapping("/r/r1")
@PreAuthorize("hasAuthority('p1')")
public String r1(){
return "访问资源1!";
}
@RequestMapping("/r/r2")
@PreAuthorize("hasAuthority('p2')")
public String r2(){
return "访问资源2!";
}
}
  • 访问r1

输入账号zhangsan密码123

 

访问r2

 

 自定义密码加密规则

  • 引入md5加密util
package com.security.oauth2.oauth2service.util;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* 1.MD5加密字符串(32位大写)
* 2.MD5加密字符串(32位小写)
* <p>
* MD5在线加密:https://md5jiami.51240.com/
* 3.将二进制字节数组转换为十六进制字符串
* 4.Unicode中文编码转换成字符串
*/

public class MD5Util {

/**
* MD5加密字符串(32位大写)
*
* @param string 需要进行MD5加密的字符串
* @return 加密后的字符串(大写)
*/

public static String md5Encrypt32Upper(String string) {
byte[] hash;
try {
//创建一个MD5算法对象,并获得MD5字节数组,16*8=128位
hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Huh, MD5 should be supported?", e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Huh, UTF-8 should be supported?", e);
}

//转换为十六进制字符串
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
if ((b & 0xFF) < 0x10){
hex.append("0");
}
hex.append(Integer.toHexString(b ÿ
}
return hex.toString().toUpperCase();
}

// public static void main(String[] args) {
// System.out.println(encodeMD5("123"));
// }
/**
* MD5加密字符串(32位小写)
*
* @param string 需要进行MD5加密的字符串
* @return 加密后的字符串(小写)
*/

public static String md5Encrypt32Lower(String string) {
byte[] hash;
try {
//创建一个MD5算法对象,并获得MD5字节数组,16*8=128位
hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Huh, MD5 should be supported?", e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Huh, UTF-8 should be supported?", e);
}

//转换为十六进制字符串
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
if ((b & 0xFF) < 0x10){
hex.append("0");
}
hex.append(Integer.toHexString(b ÿ
}
return hex.toString().toLowerCase();
}

/**
* 将二进制字节数组转换为十六进制字符串
*
* @param bytes 二进制字节数组
* @return 十六进制字符串
*/

public static String bytesToHex(byte[] bytes) {
StringBuffer hexStr = new StringBuffer();
int num;
for (int i = 0; i < bytes.length; i++) {
num = bytes[i];
if (num < 0) {
num += 256;
}
if (num < 16) {
hexStr.append("0");
}
hexStr.append(Integer.toHexString(num));
}
return hexStr.toString().toUpperCase();
}

/**
* Unicode中文编码转换成字符串
*/

public static String unicodeToString(String str) {
Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
Matcher matcher = pattern.matcher(str);
char ch;
while (matcher.find()) {
ch = (char) Integer.parseInt(matcher.group(2), 16);
str = str.replace(matcher.group(1), ch + "");
}
return str;
}

public static void main(String[] args) {
// System.out.println(md5Encrypt32Lower("oem"));
// System.out.println(md5Encrypt32Lower("djdqltj"));//大江东去浪淘尽
// System.out.println(md5Encrypt32Lower("SOC_SAFE_1149"));
// System.out.println(md5Encrypt32Lower("mhxzkhl"));//梅花香自苦寒来

// String password = DesUtil.decrypt("670B14728AD9902AECBA32E22FA4F6BD");
// System.out.println("password:"+password);
System.out.println(md5Encrypt32Upper("123"));
}
}
  • 创建MD5PasswordEncoder实现PasswordEncoder的密码验证方法
package com.security.oauth2.oauth2service.config.security;

import com.security.oauth2.oauth2service.util.MD5Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.password.PasswordEncoder;

public class MD5PasswordEncoder implements PasswordEncoder {

private Logger logger = LoggerFactory.getLogger(MD5PasswordEncoder.class);

@Override
public String encode(CharSequence rawPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("rawPassword cannot be null");
} else {
return MD5Util.md5Encrypt32Upper(rawPassword.toString());
}
}

/**
* 重写密码验证规则
*/

@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("rawPassword cannot be null");
} else if (encodedPassword != null && encodedPassword.length() != 0) {

return encode(rawPassword).equals(encodedPassword);
} else {
this.logger.warn("Empty encoded password");
return false;
}
}
@Override
public boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
  • 修改WebSecurityConfig中的密码bean

  • 启动访问

登录zhangsan   123

登录lisi 123(数据库中是加密的md5串)

 

 

 

  •  

 

举报

相关推荐

0 条评论