赞
踩
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 权限
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。