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

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

  • 相关阅读:
    Leetcode Spiral Matrix
    Leetcode Sqrt(x)
    Leetcode Pow(x,n)
    Leetcode Rotate Image
    Leetcode Multiply Strings
    Leetcode Length of Last Word
    Topcoder SRM 626 DIV2 SumOfPower
    Topcoder SRM 626 DIV2 FixedDiceGameDiv2
    Leetcode Largest Rectangle in Histogram
    Leetcode Set Matrix Zeroes
  • 原文地址:https://www.cnblogs.com/linux0kk/p/12170814.html
Copyright © 2011-2022 走看看