• Spring Security 入门学习--数据库认证和授权


    • 首先是使用的SpringBoot框架

       基础需要的pom以来如下,基础的springboot项目的创建就不一一赘述了。

            <!--spring web-->     
             <dependency>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-starter-web</artifactId>
             </dependency>
            <!--jpa 对数据库操作的框架 类似mybatis-->
             <dependency>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-starter-data-jpa</artifactId>
             </dependency>
            <!--页面引擎 thymeleaf模板-->
             <dependency>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-starter-thymeleaf</artifactId>
             </dependency>
             <!--mysql数据库驱动-->
             <dependency>
                 <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                 <scope>runtime</scope>
             </dependency> 
    • 加入必须的security依赖
           <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>

    数据库连接配置文件

    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://xx.xx.xx.xx:3306/springsecurity?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
    spring.datasource.username=user spring.datasource.password=password

    #设置运行时打印sql语句 spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=update #关闭thymeleaf缓存 spring.thymeleaf.cache=false

    #不用数据库认证时默认的用户名和密码
    #spring.security.user.name=admin
    #spring.security.user.password=1234
    • 实体类***********重要***********

      对于实体类的创建:

    1. 用户表包含权限表
    2. 用户表不包含权限表

      笔者使用的是用户表包含权限表的方式,那么不包含权限表该怎么解决呢?

        创建一个新的实体类,如SecurityUser 继承User 实现UserDetails 它包含User的内容和UserDetails的内容,对于权限的设置就不一样了

          重点注意这个方法 getAuthorities ,它来将权限交给security管理 暂时可以用Null值 在UserDetailsService中具体赋值

    @Entity
    public class User implements Serializable, UserDetails {
    
        private String username;
    
        private String password;
    
        private String role;
    
        public User(){}
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) { this.username = username; }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public String getRole() {
            return role;
        }
    
        public void setRole(String role) {
            this.role = role;
        }
    
        /**
         * 指示用户的账户是否已过期。无法验证过期的账户。
         * 如果用户的账户有效(即未过期),则返回true,如果不在有效就返回false
         */
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        /**
         * 指示用户是锁定还是解锁。无法对锁定的用户进行身份验证。
         * 如果用户未被锁定,则返回true,否则返回false
         */
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
    
        /**
         * 指示用户的凭证(密码)是否已过期。过期的凭证阻止身份验证
         * 如果用户的凭证有效(即未过期),则返回true
         * 如果不在有效(即过期),则返回false
         */
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
    
        /**
         * 指示用户是启用还是禁用。无法对禁用的用户进行身份验证
         * 如果启用了用户,则返回true,否则返回false
         */
        @Override
        public boolean isEnabled() {
            return true;
        }
    
        /**
         * 得到用户的权限,如果权限表和用户表是分开的,我们需要在重新定义一个实体类实现UserDetails 并且继承于User类
    * 交给security的权限,放在UserDetailService进行处理
    */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> authorities = new ArrayList<>(); //角色必须以ROLE_开头,如果数据库没有,则需要加上 至于角色为什么必须要以ROLE_开头 笔者没有进行深究 authorities.add(new SimpleGrantedAuthority("ROLE_"+this.role)); return authorities; } }
    •  此处就先不谈security的config配置吧,后面会提到

         我们还没有用到数据库中的数据,在此我们需要改造UserServiceImpl类,让其实现 UserDetailsService 并重写其中的 loadUserByUsername 方法,这是数据库认证的必要流程,贴代码:

    @Service
    public class UserServiceImpl implements UserService, UserDetailsService {
        @Autowired
        UserDao userDao;
    
        /**
         *  实现security认证实现的方法
         */
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            logger.error("username:" + username);
           //option这个东西请读者自行研究,此处可以直接是一个user,optional只是用于判断空值,避开空指针异常的
            Optional<User> byUsername = userDao.findByUsername(username);
            if (!byUsername.isPresent()) {
                throw new UsernameNotFoundException("用户名不存在,请先注册后登录!");
            }else{
                //权限表和用户表分开************按照下面方式,如果不是,直接返回带有权限信息的User对象
                //查询的权限在此处可以通过数据库查询,并且赋值
                //List<GrantedAuthority> authorities=new ArrayList<>();
                //authorities.add(new SimpleGrantedAuthority("ROLE_SPITTER"))
               //新创建一个SecurityUser(自定义实现了UserDetails的类)
               //将authorites放到该对象中,并返回出去
                return byUsername.get();
            }
        }
    }
    • 对于security的配置******************重要

      新创建一个配置类继承 WebSecurityConfigurerAdapter 

      需要注意的几个方法(重写的方法):

        configure(HttpSecurity http)   :     htpp请求安全处理

        configure(AuthenticationManagerBuilder auth)    :  自定义数据库权限认证

        configure(WebSecurity web)    :    WEB安全

       

    @Configuration
    @EnableWebSecurity         // 开启注解
    @EnableGlobalMethodSecurity(prePostEnabled = true)  // 开启注解
    
    
    
    //自定义了一个springSecurity安全框架的配置类 继承WebSecurityConfigurerAdapter,重写其中的方法configure,
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        UserServiceImpl userService;
    
     
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()  //接触防止跨域请求
                    .authorizeRequests()
                    .antMatchers("/toLogin","/templates/**","/logout","/toIndex").permitAll()  //忽略认证的url
                    .anyRequest().authenticated()  //其他的url都需要被认证才可以访问
                    .and()
                    .formLogin()      //允许自定义表单登录
                    .loginPage("/toLogin")  //这是action地址  不能写具体页面  确定自定义登录页  自己不需要写login方法  登录是由security提供
                    .loginProcessingUrl("/login")  //这是html中form中action的值 必须要对应
                    .defaultSuccessUrl("/toIndex")  //默认登录成功会跳转的controller
                   //关于登录失败的提示信息,请读者自行解决
                    .failureForwardUrl("/login-error")
                    .failureUrl("/login-error");
        }
    
    
         //密码加密,最新的security必须使用密码加密
        @Bean
        public PasswordEncoder passwordEncoder(){
            //使用BCcypt加密
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            //从数据库读取的用户进行身份认证 实现了userDetailsService的类
            auth.userDetailsService(userService)
                    .passwordEncoder(passwordEncoder());
        }
    
        //解除对静态资源的保护
        @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/js/**","/templates/**");
        }
    
    }
    • 接下来是Controller和页面
    • 必须要登录成功才可以访问其他的接口,没有通过认证会一直卡在login
    @Controller
    public class UserController {
        @Autowired
        UserServiceImpl userService;
    
        @RequestMapping("/toIndex")
        public String index(){
            return "index";
        }
    
        @RequestMapping("/toLogin")
        public String login(){
            return "login";
        }
        //定义需要访问的权限
        @PreAuthorize("hasAnyRole('ROLE_admin')")
        @RequestMapping("/sayHello")
        @ResponseBody
        public String sayHello(){
            return "hello";
        }
        //定义需要访问的权限
        @PreAuthorize("hasAnyAuthority('ROLE_user')")
        @RequestMapping("/sayYes")
        @ResponseBody
        public String sayYes(){
            return "yes";
        }
    
    
    }

      html页面

    <!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
          xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <!--layui css-->
        <link rel="stylesheet" th:href="@{js/layui-v2.5.5/layui/css/layui.css}">
        <!--layui js-->
        <script th:src="@{js/layui-v2.5.5/layui/layui.js}"></script>
        <!--jquery-->
        <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
        <title>登录页面</title>
    </head>
    <body>
            <!--这里请注意action的值需要与loginProcessingUrl 相对应-->
            <form action="/login" method="post">
                <div class="layui-form-item">
                    <label class="layui-form-label">输入框</label>
                    <div class="layui-input-inline">
                        <input type="text" id="username" name="username" required  lay-verify="required" placeholder="请输入用户名" autocomplete="off" class="layui-input">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">密码框</label>
                    <div class="layui-input-inline">
                        <input type="password"  name="password" required lay-verify="required" placeholder="请输入密码" autocomplete="off" class="layui-input">
                    </div>
                </div>
                <div class="layui-form-item">
                    <div class="layui-input-block">
                        <button class="layui-btn layui-btn-warm" lay-submit lay-filter="formDemo">登录</button>
                    </div>
                </div>
            </form>
    </body>
    </html>

    第一次写博客 感觉还是有点乱,请和谐讨论,笔者也是看了很多其他的博客慢慢弄出来的,希望大家不要害怕麻烦,慢慢来,引入就不贴了,看了太多 也不知道谁是谁的

  • 相关阅读:
    Nginx如何配置基础缓存
    Websocket消息过长自动断开连接?
    Docker错误删除Postgresql容器如何恢复?
    Docker安装带中文全文搜索插件zhparser的Postgresql数据库
    Postgresql数据库安装中文全文搜索插件zhparser的问题
    Presto通过RESTful接口新增Connector
    在windows的IDEA运行Presto
    Druid.io通过NiFi摄取流数据
    Druid.io SQL乱码问题
    Druid.io启用SQL支持
  • 原文地址:https://www.cnblogs.com/linux0kk/p/12170814.html
Copyright © 2020-2023  润新知