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

     

     

  • 相关阅读:
    三大主流负载均衡软件对比(LVS+Nginx+HAproxy)
    nginx 提示the "ssl" directive is deprecated, use the "listen ... ssl" directive instead
    centos安装nginx并配置SSL证书
    hadoop创建目录文件失败
    The server time zone value 'EDT' is unrecognized or represents more than one time zone.
    脚本启动SpringBoot(jar)
    centos做免密登录
    数据库远程连接配置
    Bash 快捷键
    TCP三次握手四次断开
  • 原文地址:https://www.cnblogs.com/yelao/p/12982243.html
Copyright © 2011-2022 走看看