Spring-Boot-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>

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

package com.mybatis.code.demo.entity;

public class Admin {

    private String username;
    private String password;
}
package com.mybatis.code.demo.entity;

public class Employee {
    
    private String id;
    private String username;
    private String password;
    
}

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 基础验证

    }

}

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);
    }

}

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));
    }

}

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;
        }

    }
}

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,恭喜你访问成功!";
    }

}

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值