当前位置:   article > 正文

Spring-Boot-Security实现权限管理+MD5加密(简单版本)_springboot security md5

springboot security md5

Spring-Boot-Security实现权限管理+MD5加密:

1.在pom.xml中引入SpringBoot集成SpringSecurity的依赖包

<!-- Spring-security -->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>2.2.2.RELEASE</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-actuator -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-actuator</artifactId>
    <version>2.2.2.RELEASE</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.添加实体类-权限对象Admin、Employee:
此处去除了get、set方法,可以idea自动生成,也可以引入lombok依赖包,用@Data注解生成

package com.mybatis.code.demo.entity;

public class Admin {

    private String username;
    private String password;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
package com.mybatis.code.demo.entity;

public class Employee {
    
    private String id;
    private String username;
    private String password;
    
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

3.添加SecurityConfiguration配置类,继承WebSecurityConfigurerAdapter配置角色权限:

package com.mybatis.code.demo.config;

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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new OverPasswordEncoder();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
         .passwordEncoder(passwordEncoder());
        // 这里的passwordEncoder()调用了上面的Bean,返回的是new OverPasswordEncoder()
        // passwoldEncoder是对密码的加密处理,如果user中密码没有加密,则可以不加此方法。注意加密请使用security自带的加密方式。
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()//禁用了 csrf 功能
                .authorizeRequests()//限定签名成功的请求
                //对decision和govern和employee 下的接口 需要 EMPLOYEE 或者 ADMIN 权限
                .antMatchers("/decision/**","/govern/**","/employee/**").hasAnyRole("EMPLOYEE","ADMIN")
                //对admin下的接口 需要ADMIN权限(只对admin开放)
                .antMatchers("/admin/**").hasRole("ADMIN")
                //不拦截 oauth 开放的资源(对任何权限开放,甚至没有权限)
                .antMatchers("/oauth/**").permitAll()
                //其他没有限定的请求,允许访问
                .anyRequest().permitAll()
                .and().anonymous()//对于没有配置权限的其他请求允许匿名访问
                .and().formLogin()//使用 spring security 默认登录页面
                .and().httpBasic();//启用http 基础验证

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

4.重写PasswordEncoder接口的encode、matches方法,实现MD5加密、验证:
OverPasswordEncoder.java:

package com.mybatis.code.demo.config;

import com.mybatis.code.demo.utils.MD5UtilBetter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

@Component
public class OverPasswordEncoder implements PasswordEncoder {

    @Override
    public String encode(CharSequence rawPassword) {

        String salt = MD5UtilBetter.getSalt();
        return MD5UtilBetter.generate((String)rawPassword,salt);
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        System.out.println("用户输入的密码:"+rawPassword);
        System.out.println("数据库中加密后的的密码:"+encodedPassword);
        System.out.println("用户输入密码和数据库中的密码是否相等:"+MD5UtilBetter.verify((String)rawPassword,encodedPassword));
        System.out.println("========================================");

        return MD5UtilBetter.verify((String)rawPassword,encodedPassword);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

MD5UtilBetter.java:

package com.mybatis.code.demo.utils;

import org.apache.commons.codec.binary.Hex;

import java.security.MessageDigest;
import java.util.Random;

/**
 * 一般使用的加盐:
 * md5(Password+UserName),即将用户名和密码字符串相加再MD5,这样的MD5摘要基本上不可反查。
 * 但有时候用户名可能会发生变化,发生变化后密码即不可用了(验证密码实际上就是再次计算摘要的过程)。
 * ----------
 * 因此我们做了一个非常简单的加盐算法,每次保存密码到数据库时,都生成一个随机16位数字,将这16位数字和密码相加再求MD5摘要,然后在摘要中再将这16位数字按规则掺入形成一个48位的字符串。
 * 在验证密码时再从48位字符串中按规则提取16位数字,和用户输入的密码相加再MD5。按照这种方法形成的结果肯定是不可直接反查的,且同一个密码每次保存时形成的摘要也都是不同的。
 * @Author: Csa
 */
public class MD5UtilBetter {
    private static Integer MAX = 16;  //32位随机数

    /**
     * 获取随机盐
     * 盐被称作“Salt值”,这个值是由系统随机生成的,并且只有系统知道。即便两个用户使用了同一个密码,由于系统为它们生成的salt值不同,散列值也是不同的。
     */
    public static String getSalt(){
        StringBuffer buffer = new StringBuffer("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
        StringBuffer sb = new StringBuffer();
        Random random = new Random();
        int range = buffer.length();
        for (int i = 0; i < MAX; i ++) {
            sb.append(buffer.charAt(random.nextInt(range)));
        }
        return sb.toString();
    }

    /**
     * 获取十六进制字符串形式的MD5摘要
     */
    public static String md5Hex(String src) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] bs = md5.digest(src.getBytes());
            return new String(new Hex().encode(bs));
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 生成含有随机盐的密码
     */
    public static String generate(String password, String salt) {
        password = md5Hex(password + salt);
        char[] cs = new char[48];
        for (int i = 0; i < 48; i += 3) {
            cs[i] = password.charAt(i / 3 * 2);
            char c = salt.charAt(i / 3);
            cs[i + 1] = c;
            cs[i + 2] = password.charAt(i / 3 * 2 + 1);
        }
        return new String(cs);
    }

    /**
     * 校验密码是否正确
     */
    public static boolean verify(String password, String md5) {
        char[] cs1 = new char[32];
        char[] cs2 = new char[16];
        for (int i = 0; i < 48; i += 3) {
            cs1[i / 3 * 2] = md5.charAt(i);
            cs1[i / 3 * 2 + 1] = md5.charAt(i + 2);
            cs2[i / 3] = md5.charAt(i + 1);
        }
        // 获取md5加盐加密后密码中的盐
        String salt = new String(cs2);
        System.out.println("校验----原来密码获得的md5加密加盐字符串:"+md5Hex(password + salt));
        System.out.println("校验----输入密码获得的md5加密加盐字符串:"+new String(cs1));
        return md5Hex(password + salt).equals(new String(cs1));
    }

    public static void main(String[] args) {
        String salt = getSalt();
        System.out.println("输出盐:"+salt);
        String password = generate("123456", salt);
        System.out.println("输出加盐--并用相应规则转换后的密码:"+password);
        System.out.println("输出密码长度:"+password.length());
        System.out.println("输出输入密码是否正确:"+verify("123456", password));
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90

5.创建用户UserDetailServiceImpl实现UserDetailsService接口:
此处为了方便,不连接数据库,使用假数据进行模拟,生成环境使用数据替代即可。

package com.mybatis.code.demo.service.impl;

import com.mybatis.code.demo.config.OverPasswordEncoder;
import com.mybatis.code.demo.entity.Admin;
import com.mybatis.code.demo.entity.Employee;
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 UserDetailServiceImpl implements UserDetailsService {

    @Autowired
    private OverPasswordEncoder overPasswordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();

        System.out.println("权限获取到的用户名:"+username);

        //生成环境是查询数据库获取username的角色用于后续权限判断(如:张三 admin)
        //这里不做数据库操作,给定假数据,有兴趣的人可以使内存模式。
        if (username.equals("employee")) {
            Employee employee = new Employee();
            employee.setUsername("employee");
            employee.setPassword("111111");
            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_EMPLOYEE");
            grantedAuthorities.add(grantedAuthority);
            //创建一个用户,用于判断权限,请注意此用户名和方法参数中的username一致;BCryptPasswordEncoder是用来演示加密使用。
            return new User(employee.getUsername(), overPasswordEncoder.encode(employee.getPassword()), grantedAuthorities);
        }
        if (username.equals("admin")) {
            Admin admin = new Admin();
            admin.setUsername("admin");
            admin.setPassword("123456");
            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_ADMIN");
            grantedAuthorities.add(grantedAuthority);
            return new User(admin.getUsername(), overPasswordEncoder.encode(admin.getPassword()), grantedAuthorities);
        }
        else {
            return null;
        }

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

6.编写Security权限测试Controller类:

package com.mybatis.code.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class SecurityController {

    /**
     * 其他没有限定的内容,可以访问
     * @return String
     */
    @GetMapping("/helloWorld")
    @ResponseBody
    public String helloWorld(){
        return "其他没有限定的内容,可以访问!";
    }

    /**
     * 不拦截 oauth 开放的资源
     * @return String
     */
    @GetMapping("/oauth/login")
    @ResponseBody
    public String oauthLogin() {
        return "不拦截 oauth 开放的资源!";
    }

    /**
     * 对admin下的接口 需要ADMIN权限(只对admin开放)
     * @return String
     */
    @GetMapping("/admin/greeting")
    @ResponseBody
    public String adminGreeting() {
        return "你的权限为Admin,恭喜访问成功!";
    }

    /**
     * 对decision和govern和employee 下的接口 需要 EMPLOYEE 或者 ADMIN 权限
     * @return String
     */
    @GetMapping("/employee/greeting")
    @ResponseBody
    public String employeeGreeting() {
        return "此为员工页面,你的权限为Employee 或者 Admin,恭喜你访问成功!";
    }

    /**
     * 对decision和govern和employee 下的接口 需要 EMPLOYEE 或者 ADMIN 权限
     * @return String
     */
    @GetMapping("/decision/greeting")
    @ResponseBody
    public String decisionGreeting() {
        return "此为运营页面,你的权限为Employee 或者 Admin,恭喜你访问成功!";
    }

    /**
     * 对decision和govern和employee 下的接口 需要 EMPLOYEE 或者 ADMIN 权限
     * @return String
     */
    @GetMapping("/govern/greeting")
    @ResponseBody
    public String governGreeting() {
        return "此为治理页面,你的权限为Employee 或者 Admin,恭喜你访问成功!";
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

7.使用Postman进行测试:
①.他没有限定的内容,可以访问
在这里插入图片描述
②.对decision和govern和employee 下的接口 需要 EMPLOYEE 或者 ADMIN 权限
在这里插入图片描述
在这里插入图片描述

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号