目录
- 1. SpringSecurity 框架用法简介
- 2. 权限管理过程中的相关概念
- 3. 权限管理的主流框架
- 4. 使用配置类代替 XML 配置文件
- 5. HelloWorld 工程创建步骤
- 6. 在 HelloWorld 基础上加入SpringSecurity
- 7. SpringSecurity 操作实验
1. SpringSecurity 框架用法简介
用户登录系统时我们协助 SpringSecurity 把用户对应的角色、权限组装好,同时把各个资源所要求的权限信息设定好,剩下的“登录验证”、“权限验证”等等工作都交给
SpringSecurity
2. 权限管理过程中的相关概念
2.1 主体
英文单词:principal
使用系统的用户或设备或从其他系统远程登录的用户等等。简单说就是谁使用系统谁就是主体。
2.2 认证
英文单词:authentication
权限管理系统确认一个主体的身份,允许主体进入系统。简单说就是“主体”证明自己是谁。
笼统的认为就是以前所做的登录操作。
2.3 授权
英文单词:authorization
将操作系统的“权力”“授予”“主体”,这样主体就具备了操作系统中特定功能的能力。
所以简单来说,授权就是给用户分配权限。
3. 权限管理的主流框架
3.1 SpringSecurity
Spring 技术栈的组成部分。
通过提供完整可扩展的认证和授权支持保护你的应用程序。
https://spring.io/projects/spring-security
SpringSecurity 特点:
和 Spring 无缝整合。
全面的权限控制。
专门为 Web 开发而设计。
旧版本不能脱离 Web 环境使用。新版本对整个框架进行了分层抽取,分成了核心模块和 Web 模块。单独引入核心模块就可以脱离 Web 环境。
重量级。
3.2 Shiro
Apache 旗下的轻量级权限控制框架。
特点:
轻量级。Shiro 主张的理念是把复杂的事情变简单。针对对性能有更高要求的
互联网应用有更好表现。
通用性。
好处:不局限于 Web 环境,可以脱离 Web 环境使用。
缺陷:在 Web 环境下一些特定的需求需要手动编写代码定制。
官网网址:http://shiro.apache.org/
学习视频网址:http://www.gulixueyuan.com/course/45
4. 使用配置类代替 XML 配置文件
4.1 @Configuration 注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component//由于当前注解带有@Component 注解,所以标记当前注解的类可以享受包的
自动扫描
public @interface Configuration {
/**
* Explicitly specify the name of the Spring bean definition associated
* with this Configuration class.
If left unspecified (the common case),
* a bean name will be automatically generated.
*
* <p>The custom name applies only if the Configuration class is picked up via
*
component
scanning
or
supplied
directly
to
a
{@link
AnnotationConfigApplicationContext}.
* If the Configuration class is registered as a traditional XML bean definition,
* the name/id of the bean element will take precedence.
*
* @return the specified bean name, if any
* @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
*/
String value() default "";
}
类标记了这个注解就可以使用这个类代替 Spring 的 XML 配置文件。
4.2 @Bean 注解
用来代替 XML 配置文件中的 bean 标签。下面两种形式效果一致:
<bean id="empHandler" class="com.atguigu.component.EmpHandler">
<property />
</bean>
@Configuration
public class AnnotaionConfig{
@Bean
public EmpHandler getEmpHandler(){
return new EmpHandler();
}
}
提示:Spring 通过调用标记了@Bean 注解的方法将对象放入 IOC 容器行为不会重复调用方法。原因是 Spring 想要获取 bean 对应的实例对象时会查看 IOC 容器中是否已经有了这个对象,如果有则不会执行这个方法,从而保证这个 bean 是单一实例的。
如果希望对应的 bean 是多实例的,则可以配合@Scope 注解
5. HelloWorld 工程创建步骤
5.1 创建 Maven 的 Web 工程
pro05-spring-security
grouold: com.atguigu.security
artifactld:pro05-spring-security
packaging: war
5.2 加入 SpringMVC 环境需要的依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
<!-- 引入 Servlet 容器中相关依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!-- JSP 页面使用的依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1.3-b06</version>
<scope>provided</scope>
</dependency>
5.3 创建 SpringMVC 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
">
<context:component-scan
base-package="com.atguigu.security"></context:component-scan>
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<mvc:annotation-driven></mvc:annotation-driven>
<mvc:default-servlet-handler />
</Beans>
5.4 在 web.xml 中配置 DispatcherServlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- The front controller of this Spring Web application, responsible for handling all application
requests -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
5.5 创建包
com.atguigu.security.controller
5.6 从例子工程中复制 Controller
5.7 加入 webapp 目录下文件
6. 在 HelloWorld 基础上加入SpringSecurity
6.1 加入 SpringSecurity 依赖
<!-- SpringSecurity 对 Web 应用进行权限管理 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<!-- SpringSecurity 配置 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<!-- SpringSecurity 标签库 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
6.2 加入 SpringSecurity 控制权限的 Filter
SpringSecurity 使用的是过滤器 Filter而不是拦截器 Interceptor,意味着 SpringSecurity能够管理的不仅仅是 SpringMVC 中的 handler 请求,还包含 Web 应用中所有请求。比如:项目中的静态资源也会被拦截,从而进行权限控制。
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
特 别 注 意 : springSecurityFilterChain 标 签 中 必 须 是
springSecurityFilterChain。因为 springSecurityFilterChain 在 IOC 容器中对应真正执行权限控制的二十几个 Filter,只有叫这个名字才能够加载到这些 Filter。
6.3 加入配置类
com.atguigu.security.config.WebAppSecurityConfig
@Configuration
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
}
Enable 理解为启用。
@EnableWebSecurity 注解表示启用 Web 安全功能。
以后会接触到很多@EnableXxx 注解,用来启用对应的功能。
6.4 效果
所有请求都被 SpringSecurity 拦截,要求登录才可以访问。
静态资源也都被拦截,要求登录。
登录失败有错误提示。
7. SpringSecurity 操作实验
下面的操作都是在 HelloWorld 的基础上逐步增加权限控制设置,循序渐进学习
SpringSecurity 用法。
7.1 实验 1:放行首页和静态资源
在配置类中重写父类的 configure(HttpSecurity security)方法。
protected void configure(HttpSecurity security) throws Exception {
logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially
override subclass configure(HttpSecurity).");
security
.authorizeRequests()
.anyRequest().authenticated()
//所有请求都需要进行认证
.and()
.formLogin()
.and()
.httpBasic();
}
重写后
@Override
protected void configure(HttpSecurity security) throws Exception {
//super.configure(security); 注释掉将取消父类方法中的默认规则
security.authorizeRequests()
//对请求进行授权
.antMatchers("/layui/**","/index.jsp")
//使用 ANT 风格设置要授权的 URL 地
址
.permitAll()
//允许上面使用 ANT 风格设置的全部请求
.anyRequest()
//其他未设置的全部请求
.authenticated();
//需要认证
}
效果:未认证的请求会跳转到 403 错误页面。
7.2 实验 2:未认证请求跳转到登录页
@Override
protected void configure(HttpSecurity security) throws Exception {
//super.configure(security); 注释掉将取消父类方法中的默认规则
security.authorizeRequests()
//对请求进行授权
.antMatchers("/layui/**","/index.jsp")
//使用 ANT 风格设置要授权的 URL 地
址
.permitAll()
//允许上面使用 ANT 风格设置的全部请求
.anyRequest()
//其他未设置的全部请求
.authenticated()
//需要认证
.and()
.formLogin()
//设置未授权请求跳转到登录页面
.loginPage("/index.jsp")
//指定登录页
.permitAll();
//为登录页设置所有人都可以访问
}
指定登录页前后 SpringSecurity 登录地址变化:
指定前
/login GET - the login form
/login POST - process the credentials and if valid authenticate the user
/login?error GET - redirect here for failed authentication attempts
/login?logout GET - redirect here after successfully logging out
指定后
/index.jsp GET - the login form
/index.jsp POST - process the credentials and if valid authenticate the user
/index.jsp?error GET - redirect here for failed authentication attempts
/index.jsp?logout GET - redirect here after successfully logging out
通过调用 loginProcessingUrl()方法指定登录地址。
security
.authorizeRequests()
// 对请求进行授权
……
// loginProcessingUrl()方法指定了登录地址,就会覆盖 loginPage()方法中设置的默认值
/index.jsp POST
.loginProcessingUrl("/do/login.html")
// 指定提交登录表单的地址
;
7.3 实验 3:设置登录系统的账号、密码
7.3.1 页面设置
给 index.jsp 设置表单
注意:要取消页面的“假”提交。不用管 layui 的语法。
账号、密码的请求参数名
SpringSecurity 默认账号的请求参数名:username
SpringSecurity 默认密码的请求参数名:password
要么修改页面上的表单项的 name 属性值,要么修改配置。如果修改配置可以调用
usernameParameter()和 passwordParameter()方法。
@Override
protected void configure(HttpSecurity security) throws Exception {
.usernameParameter("loginAcct") // 定制登录账号的请求参数名
.passwordParameter("userPswd") // 定制登录密码的请求参数名
}
7.3.2 后端配置
设置登录成功后默认前往的页面
@Override
protected void configure(HttpSecurity security) throws Exception {
. .authorizeRequests() // 对请求进行授权
.anyRequest() // 任意的请求
.authenticated() // 需要登录以后才可以访问
.and()
.formLogin() //使用表单形式登录
// 关于loginPage()方法的特殊说明
// 指定登录页的同时会影响到:“提交登录表单的地址”、“退出登录地址”、“登录失败地址”
// /index.jsp GET - the login form 去登录页面
// /index.jsp POST - process the credentials and if valid authenticate the user 提交登录表单
// /index.jsp?error GET - redirect here for failed authentication attempts 登录失败
// /index.jsp?logout GET - redirect here after successfully logging out 退出登录
// 指定登录页的同时会影响到:"提交登录表单的地址"、"退出登录地址"、"登录失败地址"
.loginPage("/index.jsp") // 指定登录页面(如果没有指定会访问SpringSecurity自带的登录页)
// loginProcessingUrl()方法指定了登录地址,就会覆盖loginPage()方法中设置的默认值/index.jsp POST
.loginProcessingUrl("/do/login.html") //指定提交登录表单的地址
.permitAll() // 登录地址本身也需要permitAll()放行
.usernameParameter("loginAcct") // 定制登录账号的请求参数名
.passwordParameter("userPswd") // 定制登录密码的请求参数名
.defaultSuccessUrl("/main.html") // 登录成功后前往的地址
/*.and()
.csrf()
.disable() // 禁用CSRF功能*/
.and()
}
重写另外一个父类的方法,来设置登录系统的账号密码
@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
builder
.inMemoryAuthentication() //在内存中完成账号、密码的检查
.withUser("tom") // 指定账号
.password("123123") // 指定密码
.roles("ADMIN","学徒") // 指定当前用户的角色
.and()
.withUser("jerry") // 指定账号
.password("123123") // 指定密码
.authorities("UPDATE","内门弟子") // 指定当前用户的权限
;
}
Cannot pass a null GrantedAuthority collection 问 题 是 由 于 没 有 设 置 roles() 或 authorities()方法导致的。
实现的最后效果:登录成功后具体资源都可以访问了。
7.3.3 ※了解:_csrf 如何防止跨站请求伪造?
Cross-site request forgery 跨站请求伪造
发送登录请求时没有携带_csrf 值,则返回下面错误:
从钓鱼网站的页面提交的请求无法携带正确、被承认的令牌。
7.4 实验 4:用户注销
通过调用 HttpSecurity 对象的一系列方法设置注销功能。
logout()方法:开启注销功能
logoutUrl()方法:自定义注销功能的 URL 地址
如果 CSRF 功能没有禁用,那么退出请求必须是 POST 方式。如果禁用了 CSRF
功能则任何请求方式都可以。
logoutSuccessUrl()方法:退出成功后前往的 URL 地址
addLogoutHandler()方法:添加退出处理器
logoutSuccessHandler()方法:退出成功处理器
退出的表单
<form
id="logoutForm"
action="${pageContext.request.contextPath}/my/logout"
method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
<script type="text/javascript">
window.onload = function(){
var anchor = document.getElementById("logoutAnchor");
anchor.onclick = function(){
document.getElementById("logoutForm").submit();
return false;
};
};
</script>
<a id="logoutAnchor" href="">退出</a>
7.5 实验 5:基于角色或权限进行访问控制
通过 HttpSecurity 对象设置资源的角色要求
访问被拒绝后看到 403 错误页面:
注意:调用顺序
蓝色代码设置范围更大
红色代码设置范围相对小
如果蓝色代码先调用,会把后面红色代码的设置覆盖,导致红色代码无效。所以要
先做具体小范围设置,再做大范围模糊设置。
注意:SpringSecurity 会在角色字符串前面加“ROLE_”前缀
之所以要强调这个事情,是因为将来从数据库查询得到的用户信息、角色信息、权限信息需要我们自己手动组装。手动组装时需要我们自己给角色字符串前面加“ROLE_”
前缀
7.6 实验 6:自定义 403 错误页面
由 main.jsp 复制得到 no_auth.jsp。修改如下
<div class="layui-body">
<!-- 内容主体区域 -->
<div style="padding: 15px;">
<h1>非常抱歉!您没有访问这个功能的权限!(回家照照镜子)</h1>
<h2>${message }</h2>
</div>
前往自定义页面方式一:
@RequestMapping("/to/no/auth/page.html")
public String toNoAuthPage() {
return "no_auth";
}
前往自定义页面方式二:
@Override
protected void configure(HttpSecurity security) throws Exception {
.exceptionHandling() // 指定异常处理器
// .accessDeniedPage("/to/no/auth/page.html") // 访问被拒绝时前往的页面
.accessDeniedHandler(new AccessDeniedHandlerImpl(){
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
request.setAttribute("message","抱歉!您无法访问这个资源!***");
request.getRequestDispatcher("/WEB-INF/views/no_auth.jsp").forward(request, response);
}
})
.and()
}
7.7 实验 7:记住我-内存版(不重要)
HttpSecurity 对象调用 rememberMe()方法。
登录表单携带名为 remember-me 的请求参数。具体做法是将登录表单中的checkbox 的 name 设置为 remember-me
<input type="checkbox" name="remember-me"
lay-skin="primary"
title="记住密码">
如 果 不 能 使 用 “ remember-me ” 作 为 请 求 参 数 名 称 , 可 以 使 用rememberMeParameter()方法定制。
记住我原理简要分析:
通过开发者工具看到浏览器端存储了名为 remember-me 的 Cookie。根据这个 Cookie的 value 在服务器端找到以前登录的 User
而且这个 Cookie 被设置为存储 2 个星期的时间。
7.8 实验 8:记住我-数据库版(不重要)
为了让服务器重启也不影响记住登录状态,将用户登录状态信息存入数据库。
7.8.1 建立数据库连接
加入依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!-- mysql 驱动 -->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
配置数据源
<!-- 配置数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="root"></property>
<property name="password" value="root"></property>
<property name="url" value="jdbc:mysql://localhost:3306/security?useSSL=false"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<!--jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
创建数据库
CREATE DATABASE `security` CHARACTER SET utf8;
在 WebAppSecurityConfig 类中注入数据源
@Autowired
private DataSource dataSource;
7.8.2 启用令牌仓库功能
@Override
protected void configure(HttpSecurity security) throws Exception {
dbcTokenRepositoryImpl tokenRepository = new dbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
}
注意:需要进入 JdbcTokenRepositoryImpl 类中找到创建 persistent_logins 表的 SQL 语句创建
persistent_logins 表
CREATE TABLE persistent_logins (
username VARCHAR (64) NOT NULL,
series VARCHAR (64) PRIMARY KEY,
token VARCHAR (64) NOT NULL,
last_used TIMESTAMP NOT NULL
);
7.9 实验 9:查询数据库完成认证
7.9.1 了解:SpringSecurity 默认实现
builder.jdbcAuthentication().usersByUsernameQuery(“tom”);在usersByUsernameQuery(“tom”)等方法中最终调用 JdbcDaoImpl 类的方法查询
数据库。
SpringSecurity 的默认实现已经将 SQL 语句硬编码在了 JdbcDaoImpl 类中。这种
情况下,我们有下面三种选择:
按照 JdbcDaoImpl 类中 SQL 语句设计表结构。
修改 JdbcDaoImpl 类的源码。
不使用 jdbcAuthentication()。
7.9.2 自定义数据库查询方式
builder.userDetailsService(userDetailsService)
其中userDetailsService需要自定义实现UserDetailsService接口的类并自动装配
package com.atguigu.security.config;
import com.atguigu.security.entity.Admin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
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.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Component
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private JdbcTemplate jdbcTemplate;
// 总目标: 根据表单提交的用户名查询User对象,并装配角色、权限等信息
@Override
public UserDetails loadUserByUsername(
// 表单提交的用户名
String username) throws UsernameNotFoundException {
// 1.从数据库查询Admin对象
String sql = "SELECT id,loginacct,userpswd,username,email FROM t_admin WHERE loginacct = ?";
List<Admin> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Admin.class), username);
Admin admin = list.get(0);
// 2.给Admin设置角色权限信息
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
authorities.add(new SimpleGrantedAuthority("UPDATE"));
// 3.把admin对象和authorities封装到UserDetails中
String userpswd = admin.getUserpswd();
return new User(username, userpswd, authorities);
}
}
7.9.3 使用自定义 UserDetailsService 完成登录
@Autowired
private MyUserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
//builder
//.inMemoryAuthentication()
//.withUser("tom")
// 指定登录系统的账号
//.password("123123")
// 指定账号对应的密码
//
.roles("大师");
// 必须设置角色或权限,否则会出现 Cannot pass a null
GrantedAuthority collection 错误
builder.userDetailsService(userDetailService);
}
7.9.4 “ROLE_”前缀问题
org.springframework.security.core.authority.AuthorityUtils.createAuthorityList(String…)工具方法获取创建 SimpleGrantedAuthority 对象添加角色时需要手动在角色名称前
加“ROLE_”前缀
7.10实验 10:应用自定义密码加密规则
自定义类实现 org.springframework.security.crypto.password.PasswordEncoder(使用没有过时的)接口。
package com.atguigu.security.config;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Objects;
@Component
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return privateEncode(rawPassword);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
// 1.明文密码进行加密
String formPassword = privateEncode(rawPassword);
// 2.声明数据库密码
String databasePassword = encodedPassword;
// 3.比较
return Objects.equals(formPassword, databasePassword);
}
private String privateEncode(CharSequence rawPassword){
// 1.创建MessageDigest对象
try {
// 1.创建MseeageDigest对象
String algorithm = "MD5";
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
// 2.获取rawPassword的字节数组
byte[] input = ((String)rawPassword).getBytes();
// 3.加密
byte[] output = messageDigest.digest(input);
// 4.转换为16进制数对应的字符
String encoded = new BigInteger(1, output).toString(16).toUpperCase();
return encoded;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
}
encode()方法对明文进行加密。
matches()方法对明文加密后和密文进行比较。
在配置类中的 configure(AuthenticationManagerBuilder)方法中应用自定义密码加密规则
@Autowired
private BCryptPasswordEncoder passwordEncoder;
// 每次调用这个方法时会检查IOC容器中是否有了对应的bean,如果有就不会真正执行这个函数,因为bean默认是单例的
//可以使用@Scope(value = "")注解控制是否单例
@Bean
public BCryptPasswordEncoder getBCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
// 装配userDetailsServiced对象
builder
.userDetailsService(userDetailsService).passwordEncoder(getBCryptPasswordEncoder());
}
SpringSecurity 提供的 BCryptPasswordEncoder 加密规则。
BCryptPasswordEncoder 创 建 对 象 后 代 替 自 定 义 passwordEncoder 对 象 即 可 。BCryptPasswordEncoder 在加密时通过加入随机盐值让每一次的加密结果都不同。能够
避免密码的明文被猜到。
而在对明文和密文进行比较时,BCryptPasswordEncoder 会在密文的固定位置取出盐值,重新进行加密。
测试代码
package com.atguigu.spring.security;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class SecurityTest {
public static void main(String[] args) {
// 1.创建BCryptPasswordEncoder对象
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
// 2.准备明文字符串
String rawPassword = "123123";
// 3.加密
String encode = passwordEncoder.encode(rawPassword);
System.out.println(encode);
}
}
class EncodeTest{
public static void main(String[] args) {
// 1.准备明文字符串
String rawPassword = "123123";
// 2.准备密文字符串
String encodedPassword = "$2a$10$FfLBRPb3VSHvskcdsuE5gOvnZPZZlUV8vX54y4o7oR8qA7bsHFhEG";
// 3.创建BCryptPasswordEncoder
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
// 3.比较
boolean matcheResult = passwordEncoder.matches(rawPassword, encodedPassword);
System.out.println(matcheResult ? "一致" : "不一致");
}
}