基於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);
    }

}
相關文章
相關標籤/搜索