Security整合spring boot

1、基础概念

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring的IOC,DI,AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为安全控制编写大量重复代码的工作。

2、核心API解读

1、SecurityContextHolder

最基本的对象,保存着当前会话用户认证,权限,鉴权等核心数据。SecurityContextHolder默认使用ThreadLocal策略来存储认证信息,与线程绑定的策略。用户退出时,自动清除当前线程的认证信息。

初始化源码:明显使用ThreadLocal线程。

private static void initialize() {
    if (!StringUtils.hasText(strategyName)) {
        strategyName = "MODE_THREADLOCAL";
    }
    if (strategyName.equals("MODE_THREADLOCAL")) {
        strategy = new ThreadLocalSecurityContextHolderStrategy();
    } else if (strategyName.equals("MODE_INHERITABLETHREADLOCAL")) {
        strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
    } else if (strategyName.equals("MODE_GLOBAL")) {
        strategy = new GlobalSecurityContextHolderStrategy();
    } else {
        try {
            Class<?> clazz = Class.forName(strategyName);
            Constructor<?> customStrategy = clazz.getConstructor();
            strategy = (SecurityContextHolderStrategy)customStrategy.newInstance();
        } catch (Exception var2) {
            ReflectionUtils.handleReflectionException(var2);
        }
    }
    ++initializeCount;
}

2、Authentication

源码

public interface Authentication extends Principal, Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
    Object getCredentials();
    Object getDetails();
    Object getPrincipal();
    boolean isAuthenticated();
    void setAuthenticated(boolean var1) throws IllegalArgumentException;
}

源码分析

1)、getAuthorities,权限列表,通常是代表权限的字符串集合;
2)、getCredentials,密码,认证之后会移出,来保证安全性;
3)、getDetails,请求的细节参数;
4)、getPrincipal, 核心身份信息,一般返回UserDetails的实现类。

3、UserDetails

封装了用户的详细的信息。

public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
    String getPassword();
    String getUsername();
    boolean isAccountNonExpired();
    boolean isAccountNonLocked();
    boolean isCredentialsNonExpired();
    boolean isEnabled();
}

4、 UserDetailsService

实现该接口,自定义用户认证流程,通常读取数据库,对比用户的登录信息,完成认证,授权。

public interface UserDetailsService {
    UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}

5、 AuthenticationManager

认证流程顶级接口。可以通过实现AuthenticationManager接口来自定义自己的认证方式

Spring提供了一个默认的实现,ProviderManager。

public interface AuthenticationManager {
    Authentication authenticate(Authentication var1) throws AuthenticationException;
}

3.整合

1、流程描述

1)、模拟增删改查
2)、未登录授权都不可以访问
3)、登录后根据用户权限,访问指定页面
4)、对于未授权页面,访问返回403:资源不可用

2、核心依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

3、核心配置

package com.Boot.config;

import com.Boot.Security.UserDetailServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * @Classname SpringSecurityConfig
 * @Description TODO
 * @Date 2019/11/6 19:59
 * @Created by 远
 */
@Configuration
@EnableWebSecurity //启动springSecurity启动链
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * @return
     * @Author 远
     * @Description 代替认证管理器
     * @Param
     **/
    /**
     * 自定义认证数据源
     */
    @Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception{
        builder.userDetailsService(userDetailService())
                .passwordEncoder(passwordEncoder());
    }
    @Bean
    public UserDetailServiceImpl userDetailService (){
        return new UserDetailServiceImpl () ;
    }
    /**
     * 密码加密
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * @Author 远
     * @Description 硬编码一个用户用于测试
     * @Param
     * @return
     **/
    /*@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("ljw").password("123").authorities("ROLE_ADD");
    }*/

    /**
     * @return
     * @Author 远
     * @Description 代替之前配置<security:http></security:http>
     **/
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
            //antMatchers指定页面,hasAnyAuthority指定拥有什么样的权限的用户可以访问
                .antMatchers("/add").hasAnyAuthority("ROLE_ADD")
                .antMatchers("/update").hasAnyAuthority("ROLE_UPDATE")
                .antMatchers("/list").hasAnyAuthority("ROLE_SELECT")
                .antMatchers("/delete").hasAnyAuthority("ROLE_DELETE")
            //放行index和login页面
                .antMatchers("/login").permitAll()
                .antMatchers("/index").permitAll()
            //拦截全部
                .antMatchers("/**")
                .fullyAuthenticated()
                .and()
            //解决页面csrf报错
                .csrf().disable();
         // 配置登录功能
        http.formLogin().usernameParameter("user")
                .passwordParameter("pwd")
                .loginPage("/userLogin");
        // 注销成功跳转首页
        http.logout().logoutSuccessUrl("/");
        //开启记住我功能
        http.rememberMe().rememberMeParameter("remeber");
    }
}

4、认证

package com.Boot.Security;


import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @Classname UserDetailServiceImpl
 * @Description 认证
 * @Date 2019/11/6 22:22
 * @Created by 远
 */
@Service
public class UserDetailServiceImpl implements UserDetailsService {
    @Resource
    private UserRoleMapper userRoleMapper ;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 这里可以捕获异常,使用异常映射,抛出指定的提示信息
        // 用户校验的操作
        // 假设密码是数据库查询的 123
        String password = "$2a$10$XcigeMfToGQ2bqRToFtUi.sG1V.HhrJV6RBjji1yncXReSNNIPl1K";
        // 假设角色是数据库查询的
        //查询数据里所有的用户对应角色的权限
        List<String> roleList = userRoleMapper.selectByUserName(username) ;
        List<GrantedAuthority> grantedAuthorityList = new ArrayList<>() ;
        /*
         * Spring Boot 2.0 版本踩坑
         * 必须要 ROLE_ 前缀, 因为 hasRole("LEVEL1")判断时会自动加上ROLE_前缀变成 ROLE_LEVEL1 ,
         * 如果不加前缀一般就会出现403错误
         * 在给用户赋权限时,数据库存储必须是完整的权限标识ROLE_LEVEL1
         */
        if (roleList != null && roleList.size()>0){
            for (String role : roleList){
                //将所有的角色遍历到GrantedAuthority
                grantedAuthorityList.add(new SimpleGrantedAuthority(role)) ;
            }
        }
        //返回用户,用户的用户名,密码,以及权限
        return new User(username,password,grantedAuthorityList);
    }
}

5、接口

package com.Boot.Controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @Classname productController
 * @Description 增删改查
 * @Date 2019/11/1 14:41
 * @Created by 远
 */


@Controller
public class productController {
    /**
     * @Author 远
     * @Description
     * @Param
     * @return
     **/
    @RequestMapping("/add")
    public String add() {
        return "product/add";
    }
    /**
     * @Author 远
     * @Description 商品修改
     **/
    @RequestMapping("update")
    public String Update() {
        return "product/update";
    }

    /**
     * @Author 远
     * @Description 商品显示
     **/
    @RequestMapping("list")
    public String list() {
        return "product/list";
    }

    /**
     * @Author 远
     * @Description 商品删除
     **/
    @RequestMapping("delete")
    public String del() {
        return "product/delete";
    }
     @RequestMapping("/login")
    public String login(){
        return "login";
    }
    @RequestMapping("index")
    public String index(){
        return"index";
    }
    @RequestMapping("403")
    public String forbidden(){
        return "403";
    }



}

6、前端登录页面

这里要和Security的配置文件相对应。

<div align="center">
    <form th:action="@{/userLogin}" method="post">
        用户名:<input name="user"/><br>
        密&nbsp;&nbsp;&nbsp;码:<input name="pwd"><br/>
        <input type="checkbox" name="remeber"> 记住我<br/>
        <input type="submit" value="Login">
    </form>
</div>
posted @ 2019-11-06 23:01  我的有趣住在无趣里  阅读(347)  评论(0编辑  收藏  举报