基于springboot的security机制(自定义登陆页面+基于内存身份认证+基于mybatis身份认证)简化版

上一篇文章中因为是第一次使用security这种机制。有些操做不太完备或者是为了实现某个功能走的弯路较多,这一章节是对上一章节的概括总结。固然也还有不完善地方。后期再应用中不断更新

security

安全机制html

1. 入门使用

1.1 使用maven配置最简单的security

<dependencies>
    <!-- 安全校验 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
</dependencies>

1.2 源代码

git clone https://github.com/spring-projects/spring-security.git

1.3 Java配置

WebSecurityConfigurerAdapter 提供了默认安全校验机制的配置
查看源码:git

protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .anyRequest().authenticated()    //任何请求须要进行身份校验
            .and()    
        .formLogin()                        //提交表单验证方式
            .and()
        .httpBasic();
}

为了改变其默认行为,咱们能够提供一个子类覆盖WebSecurityConfigurerAdapter的默认行为github

设置不一样的拦截权限

@EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .authorizeRequests()
            .antMatchers("/public/**").permitAll()
            .antMatchers("/students/**").hasRole("ADMIN")
        .and()
        .formLogin()
            .and()
        .httpBasic();
        
    }
}

自定义登陆页面

进行web安全配置web

@EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .authorizeRequests()
            .antMatchers("/public/**").permitAll()            //受权请求
            .antMatchers("/students/**").hasRole("ADMIN")    //受权请求
        .and()
        .formLogin()
            .loginPage("/login")    //自定义登陆页面
            .permitAll()            //容许全部人访问该路由
        .and()
        .csrf()
            .disable()                //暂时禁用csrc不然没法提交
            .httpBasic();
    }
}

添加/login跳转服务spring

@Controller
public class IndexController {
    @GetMapping("/error")
    public String error() {
        return "redirect:/error_page.html";
    }
    
    @GetMapping("/login")
    public String login(Model model, @RequestParam(value = "error", required = false) String error) {
        if (error != null) {
            model.addAttribute("error", "用户名或密码错误");
        }
        return "forward:/login_page.html";
    }
}

提供登陆页面数据库

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登陆页面</title>
</head>
<body>
<h2>自定义登陆页面</h2>
<hr>
<form action="/login" method="POST" name="f">
    用户名<input type="text" name="username"/> <br>
    密码 <input type="password" name="password"> <br>
    <input type="submit" value="登陆">
</form>
</body>
</html>

1.4 登出

默认访问'/logout'即完成退出功能,默认security机制会进行以下操做安全

  • 使HttpSession无效
  • 清理记住密码
  • 清理SecurityContextHolder
  • 重定向/login?logout

固然,咱们也能够自定义退出功能session

@EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .authorizeRequests()
            .antMatchers("/public/**").permitAll()
            .antMatchers("/students/**").hasRole("ADMIN")
        .and()
        .formLogin()
            .loginPage("/login")    //自定义登陆页面
            .permitAll()            //容许全部人访问该路由
        .and()
        .logout()                    //自定义登出操做
        .logoutUrl("/logout")                                                 
        .logoutSuccessUrl("/login")                                           
        .logoutSuccessHandler((req,resp,auth)->{
            //登出成功处理函数
            System.out.println("logout success");
            resp.sendRedirect("/login?logout");
        })                              
        .invalidateHttpSession(true)                                             
        .addLogoutHandler((req,resp,auth)->{
            //登出处理函数
            System.out.println("logout------");
        })                                         
        //.deleteCookies("")      
        .and()
        .csrf()
            .disable()                //暂时禁用csrc不然没法提交
            .httpBasic();
    }
}

1.5 身份验证

1.5.1 基于内存的身份验证

重写用户服务app

/**
     * 用户认证服务
     * */
    @Bean
    @Override
    protected UserDetailsService userDetailsService() {
        //建立基于内存用户管理对象
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        //自定义权限
        Collection<GrantedAuthority> adminAuth = new ArrayList<>();
        adminAuth.add(new SimpleGrantedAuthority("ADMIN"));
        //自定义用户
        User u = new User("terry", "123321", true, true, true, true, adminAuth);
        manager.createUser(u);
        return manager;
    }

1.5.2 基于JDBC的身份验证

作法与基于内存相似,不过基于内存是本身建立用户对象,而基于jdbc须要从数据库中查询数据而后为身份验证提供数据。实际上验证操做仍是有security来完成,咱们无非是给security提供用户信息而已。maven

@EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .authorizeRequests()
            .anyRequest().authenticated()
        .and()
        .formLogin()
            .failureHandler((req,resp,authException)->{
                System.out.println(authException.getMessage());
                resp.sendRedirect("/login?error");
            })
            .loginPage("/login")    //自定义登陆页面
            .successHandler((req,resp,auth)->{
                //获取登陆者信息
                Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
                if (principal != null && principal instanceof UserDetails) {
                    UserDetails user = (UserDetails) principal;
                    System.out.println("login success:"+user.getUsername());
                    //维护在session中
                    req.getSession().setAttribute("userDetail", user);
                    resp.sendRedirect("/");
                } 
            })
            .permitAll()            //容许全部人访问该路由
        .and()
        .logout()                    //自定义登出操做
        .logoutUrl("/logout")                                                 
        .logoutSuccessUrl("/login")                                           
        .logoutSuccessHandler((req,resp,auth)->{
            //登出成功处理函数
            System.out.println("logout success");
            resp.sendRedirect("/login?logout");
        })                              
        .invalidateHttpSession(true)                                             
        .addLogoutHandler((req,resp,auth)->{
            //登出处理函数
            System.out.println("logout------");
        })                                         
        //.deleteCookies("")      
        .and()
        .csrf()
            .disable()                //暂时禁用csrc不然没法提交
            .httpBasic();
    }
    
    
    /**
     * 基于JDBC用户认证服务
     * */
    @Bean
    @Override
    protected UserDetailsService userDetailsService() {
        
        return new UserDetailsService() {
            @Autowired
            private UserMapper userMapper;
            @Autowired
            private UserRoleMapper userRoleMapper;
            
            @Override
            public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
                
                UserDetails userDetails = null;
                try {
                    User user = userMapper.findByUsername(name);
                    if(user != null) {
                        List<UserRole> urs = userRoleMapper.findByUserId(user.getId());
                        Collection<GrantedAuthority> authorities = new ArrayList<>();
                        for(UserRole ur : urs) {
                            String roleName = ur.getRole().getName();
                            SimpleGrantedAuthority grant = new SimpleGrantedAuthority(roleName);
                            authorities.add(grant);
                        }
                        //封装自定义UserDetails类
                        userDetails = new MyUserDetails(user, authorities);
                    } else {
                        throw new UsernameNotFoundException("该用户不存在!");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return userDetails;
            }
        };
    }
}

/**
 * 自定义用户身份信息
 * */
class MyUserDetails implements UserDetails {
    // 用户信息
    private User user;
    // 用户角色
    private Collection<? extends GrantedAuthority> authorities;
    
    public MyUserDetails(User user, Collection<? extends GrantedAuthority> authorities) {
        super();
        this.user = user;
        this.authorities = authorities;
    }

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        
        return authorities;
    }

    @Override
    public String getPassword() {
        return this.user.getPassword();
    }

    @Override
    public String getUsername() {
        return this.user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return !this.user.getState().equals(User.STATE_ACCOUNTEXPIRED);
    }

    @Override
    public boolean isAccountNonLocked() {
        return !this.user.getState().equals(User.STATE_LOCK);
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return !this.user.getState().equals(User.STATE_TOKENEXPIRED);
    }

    @Override
    public boolean isEnabled() {
        return !this.user.getState().equals(User.STATE_NORMAL);
    }

}
相关文章
相关标签/搜索