• SpringSecurity之学习路途


    Spring Security 学习之旅开端

    SpringSecurity 开始

    项目:Github

    1. 引入依赖

       <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.4.1</version>
            <relativePath/> 
       </parent>
      <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-security</artifactId>
      </dependency>
    

    在manven依赖仓库中:

    2. 配置Security

    1.在包下创建SecurityCconfig类,重写configure方法,其中WebSecurity web,可以定义忽略路径

     @Override
        public void configure(WebSecurity web) throws Exception {
            //忽略拦截
            web.ignoring().antMatchers("/sayHello","/doLogin");
        }
    
    1. HttpSecurity http 可以拦截请求,可以定义登录、登出等等
     @Override
        protected void configure(HttpSecurity http) throws Exception {
           http.authorizeRequests()//开启登录
                   //表示访问,ex/index 这个接口,需要具备admin角色
                    .antMatchers("/es/**").hasRole("admin")
                   //表示剩余的其他接口,登录之后能访问
                    .anyRequest().authenticated()
                    .and()
                    .formLogin()
                    .loginPage("/login")
                   //登录处理接口
                    .loginProcessingUrl("/doLogin")
                   //定义登录时,用户名的key,默认为username
                    .usernameParameter("username")
                   //定义登录时,用户密码的key,默认为password
                    .passwordParameter("password")
                   //定义登录成功的处理器
                    .successHandler(new AuthenticationSuccessHandler() {
                        @Override
                        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                            response.setContentType("application/json;charset=utf-8");
                            response.sendRedirect("/success.html");//重定向到一个页面
                            MyUserDetails detail= (MyUserDetails)authentication.getPrincipal();
                            System.out.println(detail);
                        }
                    })
                    .failureHandler(new AuthenticationFailureHandler() {
                        @Override
                        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                            response.setContentType("application/json;charset=utf-8");
                            PrintWriter out = response.getWriter();
                            ResponseBean responseBean = ResponseBean.sendByCode("you have login failure !", 401);
                            String result = new ObjectMapper().writeValueAsString(responseBean);
                            out.write(result);
                            out.flush();
                        }
                    })
                   //和表单登录相关的接口统统都直接通过
                    .permitAll()
                    .and()
                    .logout()
                    .logoutUrl("/logout")
                    .logoutSuccessHandler(new LogoutSuccessHandler() {
                        @Override
                        public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                            response.setContentType("application/json;charset=utf-8");
                            PrintWriter out = response.getWriter();
                            out.write("you have login out success !");
                            out.flush();
                        }
                    })
                    .permitAll()
                    .and()
                    .httpBasic()
                    .and()
                    .csrf().disable();
    
        }
    

    简单的表单登录配置,这里的logou是Get请求,若要Post请求,则增加一行

    logoutRequestMatcher(new AntPathRequestMatcher("/logout","POST"))
    

    and相当于ssm中标签的结束,permitAll表示登录相关的页面、操作不要拦截。

    1. 定义密码加密
      由于security自带盐,用明文加密的都不一样,省去了我们很多时间。
        @Bean
        public PasswordEncoder passwordEncoder(){
            return new BCryptPasswordEncoder();
        }
    
    1. 引入JPA依赖
     <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
     </dependency>
            <!-- jpa -->
      <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
     <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.10</version>
     </dependency>
    
    1. 创建Use类
    @Data
    @Entity(name = "t_user")
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
        private String username;
        private String password;
        private Integer enabled;
        private Integer locked;
    }
    

    1. 创建Role类,为MyDetail作准备
    @Data
    @Entity(name = "t_role")
    public class Role {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
        private String name;
        private String chineseName;
    }
    

    1. 创建MyDetail类,实现UserDetail
    @Data
    public class MyUserDetails implements UserDetails {
    
        private User user;
    
        private List<Role> roles;
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            List<SimpleGrantedAuthority> authorities = new ArrayList<>();
            if (roles != null && roles.size() > 0) {
                for (Role role : roles) {
                    authorities.add(new SimpleGrantedAuthority(role.getName()));
                }
            }
            return authorities;
        }
    
        @Override
        public String getPassword() {
            return user == null ? null : user.getPassword();
        }
    
        @Override
        public String getUsername() {
            return user == null ? null : user.getUsername();
        }
    
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        @Override
        public boolean isAccountNonLocked() {
            Integer locked = user.getLocked();
            if (locked == 0) {
                return true;
            }
            return false;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        @Override
        public boolean isEnabled() {
            Integer enabled = user.getEnabled();
            if (enabled == 1) {
                return true;
            }
            return false;
        }
    
        @Override
        public String toString() {
            return "MyUserDetails{" +
                    "user=" + user +
                    ", roles=" + roles +
                    '}';
        }
    }
    
    1. 编写注入实体方法
      在测试类中,创建对象,jpa会自动去创建表格。做一个示范,由于学习,我先创建的表格,你们可以少走弯路了。比如下面这样:

      实体,加注解
    @Data
    @Entity(name = "t_hill_heavy")
    public class HillHeavy {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
        private String username;
        private Boolean handsome;
        private String gender;
        private Integer high;
        private boolean rich;
    
    

    创建Dao

    public interface HillHeavyDao extends JpaRepository<HillHeavy,Integer> {
        /**
         * 查询
         * @param username 用户名
         * @author 山沉
         * @date 2020/12/28 22:42
         * @return {@link HillHeavy}
         */
        HillHeavy findHillHeavyByUsername(String username);
    }
    

    在测试类中:

    @SpringBootTest
    class EsSearchApplicationTests {
        private static final Logger logger = LoggerFactory.getLogger(EsSearchApplicationTests.class);
    
        @Resource
        private HillHeavyDao hillHeavyDao;
    
        @Test
        void contextLoads() {
            HillHeavy hillHeavy = new HillHeavy();
            hillHeavy.setUsername("山沉");
            hillHeavy.setHandsome(true);
            hillHeavy.setHigh(180);
            hillHeavy.setGender("男");
            hillHeavy.setRich(true);
            hillHeavyDao.save(hillHeavy);
            logger.info("实体----->{}",hillHeavy);
        }
    
    }
    

    这样在用dao层去注入实体,在数据库中,也会生成表格,数据。如下:

    是有点自恋,啊。原谅想帅的小胖子。

    1. 有了表结构,在UserServiceImpl类,去实现UserDetailService类,重写loadUserByUserName(String username)

      在此方法中,从数据库中根据username,查询出用户,角色,返回UserDetail对象。
     @Override
     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
         User user = userMapper.selectUser(username);
         MyUserDetails details = new MyUserDetails();
         if(user == null){
             throw new BadCredentialsException("this username or password is not true!");
         }
         details.setUser(user);
         Integer id = user.getId();
         List<Role> roles = userMapper.selectRole(id);
         details.setRoles(roles);
         return details;
     }
    

    3. 测试登录

    到此,我们的表单登录,就成功了。通过doLogin登录,携带user信息,进入UsernamePasswordAuthenticationFilter 中,注入用户信息。在从表中获得用户信息与之对比,然后通过登录成功或失败返回给前端JSON格式。

    在这里说下,successHandler 是比较强大的,在里面可以作重定向,也可以获取用户信息,等等。集成 defaultSuccessUrl() successForwardUrl()

    1. defaultSuccessUrl 可以指定登录成功的跳转页面,比如输入 welcome/say,来到login.html 页面。登录成功后,会来的welcome/say

    2. sccessForwardUrl 指定登录成功后,到那里。不管登录前你操作的哪一个接口,成功后一律到指定的路径。

    测试登录

    弯弯月亮,只为美好的自己。
  • 相关阅读:
    响应式布局
    Fiddler2汉化版使用说明
    nonmember,nonfriend替换member函数
    Java回顾之Spring基础
    dudu,想在cnblogs首页看很久以前的文章不行。
    基于Nios II内核的项目程序为什么越优化越慢?
    学习 easyui:禁用 linkbutton 问题
    Socket编程 (异步通讯,解决Tcp粘包) 3
    .NET:可扩展的单据编号生成器 之 基于缓冲区的顺序号
    淘宝API应用开发
  • 原文地址:https://www.cnblogs.com/Choleen/p/14209202.html
Copyright © 2020-2023  润新知