Spring Security问题记录
Posted i野老i
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Security问题记录相关的知识,希望对你有一定的参考价值。
用户权限认证这一块一直是自己的一个盲点,之前的web demo都是通过用户名密码匹配做简单的登录认证。
最近觉得应该去了解规范的用户及权限认证技术了,从Spring Security开始学习使用。
本文记录学习Spring Security过程中遇到的一些问题及解决方案。
1. 版本问题
Spring Boot 2.x和Spring Security 5.x前配置用户名密码:
security.user.name=admin
security.user.password=admin
Spring Boot 2.x和Spring Security 5.x前禁用认证:
security.basic.enabled=false
management.security.enabled=false
Spring Boot 2.x和Spring Security 5.x后配置用户名密码:
spring.security.user.name=admin
spring.security.user.password=admin
Spring Boot 2.x和Spring Security 5.x后禁用认证:
Spring Boot 2.x和Spring Security 5.x后以下配置均失效
security.basic.authorize-mode security.basic.enabled security.basic.path security.basic.realm security.enable-csrf security.headers.cache security.headers.content-security-policyjun security.headers.content-security-policy-mode security.headers.content-type security.headers.frame security.headers.hsts security.headers.xss security.ignored security.require-ssl security.sessions
可以使用如下方式配置禁止认证:
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class}) public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class,args); } }
也可以使用:
@EnableAutoConfiguration(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class}) public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class,args); } }
2. 配置用户名密码及角色
配置文件配置如上;
java代码配置:
@Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { /** * 配置请求相关 * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { System.out.println("HttpSecurity"); http.authorizeRequests() // 需要的权限为 ROLE_USER,没有显式的表达出来 .antMatchers("/product/**").hasRole("USER") // 匹配某些请求路径,所需要的角色,配置角色权限 .antMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated().and() // 所有请求认证 .formLogin().and().httpBasic(); } /** * 配置用户角色 * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { /** * 创建某些用户,给这些用户赋予权限 */ auth.inMemoryAuthentication() .withUser("admin1") .password("admin1") .roles("ADMIN","USER") .and() .withUser("user1") .password("user1") .roles("USER"); } }
3. 来用数据源的用户信息及认证
将用户信息存于数据库中,配置数据源。
核心在于实现UserDetailsService接口,并实现UserDetails loadUserByUsername(String login) throws UsernameNotFoundException方法;
这个方法中的核心逻辑为根据login在数据库中查询用户信息,并组装成
org.springframework.security.core.userdetails.User(login,
userFromDatabase.getPassword(), grantedAuthorities);
此User为UserDetails的子类。
package cn.wqz.study.springboot.security.service.impl; import cn.wqz.study.springboot.security.dao.UserRepository; import cn.wqz.study.springboot.security.model.User; 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.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.Collection; /** * 从数据库中读取用户信息 * 根据用户信息组装成security框架所需要的用户格式 * 如封装权限,封装password等 */ @Component("userDetailsService") public class UserDetailsServiceImpl implements UserDetailsService { @Autowired UserRepository userRepository; @Override public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException { System.out.println("user:" + login); // 1. 查询用户 User userFromDatabase = userRepository.findOneByLogin(login); if (userFromDatabase == null) { //log.warn("User: {} not found", login); throw new UsernameNotFoundException("User " + login + " was not found in db"); //这里找不到必须抛异常 } // 2. 设置角色 Collection<GrantedAuthority> grantedAuthorities = new ArrayList(); GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(userFromDatabase.getRole()); grantedAuthorities.add(grantedAuthority); // 3. 创建一个User, 其是UserDetails的子类 return new org.springframework.security.core.userdetails.User(login, userFromDatabase.getPassword(), grantedAuthorities); } }
然后在 WebSecurityConfigurerAdapter 中配置 UserDetailsService 服务:
/** * 配置用户角色 * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 设置自定义的UserDetailsService,从库中获取标准的用户信息,并进行配置,发挥作用 System.out.println("AuthenticationManagerBuilder"); auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } /** * 用户密码使用加密机制保护 * @return */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
其中BCryptPasswordEncoder负责用户密码的加密,在数据库中存储的密码为原密码经过BCryptPasswordEncoder加密后的密码密文。
当请求传来密码时与数据库中的密文经过BCryptPasswordEncoder处理后完成比较认证。
4. 关于Security请求 403
集成 Spring Security,成功登录后,访问拥有权限的页面,仍然报403错误
通过th:action="@{/api/user}" 或<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>处理:
<!DOCTYPE html> <html lang="en" xmlns:th="http://thymeleaf.org"> <head> <meta charset="UTF-8"/> <title>index</title> </head> <body> <!-- @{/api/user} 解决post请求 403的问题--> <form th:action="@{/api/user}" method="post"> <input name="id" type="text"/> <input name="login" type="text"/> <input name="password" type="password"/> <input name="role" type="text"/> <input type="submit"/> </form> <form action="/api/hello" method="post"> name:<input type="text" name="name" id="name"/> <!-- 解决post请求 403的问题--> <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/> <input type="submit"/> </form> <a href="/product/info">product info get link</a> </body> </html>
5. 角色问题USER/ROLE_USER
当配置api需要USER权限时,在数据库中取到权限字符串“USER”,但是并不能成功获取到权限。
这属于框架的一个潜规则,需要用户的权限字符串前缀“ROLE_”,即在数据库中保存的权限应该为“ROLE_USER”。
-------end--------
以上是关于Spring Security问题记录的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Spring Security 在代码中获取用户的授权凭据?
Spring Security 3 日志记录不适用于 Eclipse 中的 GAE