zoukankan      html  css  js  c++  java
  • 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--------

     

     

  • 相关阅读:
    debug error 错误日志的调试模式
    fork(2)
    Fundamental theorem of arithmetic 为什么1不是质数
    Compile-time Dependency Injection With Go Cloud's Wire 编译时依赖注入 运行时依赖注入
    LevelDB
    MySQL Bugs: #34354: Feature request: EXPLAIN ALTER TABLE https://bugs.mysql.com/bug.php?id=34354
    explain 分析 聚合统计语句的性能
    (原创)《Android编程权威指南》学习笔记01-- Android应用初体验--005
    (原创)《Android编程权威指南》学习笔记01-- Android应用初体验--004
    (原创)《Android编程权威指南》学习笔记01-- Android应用初体验--003
  • 原文地址:https://www.cnblogs.com/yelao/p/12982243.html
Copyright © 2011-2022 走看看