SpringBoot使用SpringSecurity自定义用户登录与自定义权限-session篇

    SpringSecurity的学习成本比较高,API比较难懂,这里记录一下用SpringSecurity实现项目的登录各种需求

    首先实现自定义用户登录与自定义权限的session模式,session其实就是存储在服务器上的用户信息,用户登录后会返回一个sessionID存储到用户浏览器的cookie里,用户下次访问携带sessionID,服务器就能知道用户是否已经登录过。

    那么SpringSecurity是如何实现用户登录和权限校验的呢?下面贴出主要代码:

    先贴pom依赖

        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            org.springframework.cloud
            spring-cloud-starter-security
        
        
        
            org.springframework.boot
            spring-boot-starter-jdbc
        
        
            com.baomidou
            mybatisplus-spring-boot-starter
            1.0.5
        
        
            com.baomidou
            mybatis-plus
            2.1.8
        

        
            mysql
            mysql-connector-java
            5.1.39
        
        
            com.alibaba
            druid
            1.0.26
        

    这里使用的SpringBoot版本是2.1.0.RELEASE,引入了SpringSecurity和mybatis,web,等

    项目目录

    SpringBoot使用SpringSecurity自定义用户登录与自定义权限-session篇_第1张图片

    不要在意项目名称,这个本来是oauth2.0的项目,为了方便在这个项目上改的,所以才会起名叫oauth

    看目录结构,目录比较多,但是和security相关的目录只有2个,config和security

    config目录里只有一个文件,SecurityConfig,处理逻辑都是写到这里的,贴代码:

/**
 * @program: springcloud
 * @description: SpringSecurity的配置文件
 * @author: paddy
 * @create: 2019-04-15 12:47
 **/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)  //  启用方法级别的权限认证
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private MyUserDetailsService myUserDetailsService;
    @Autowired
    private MyInvocationSecurityMetadataSourceService myInvocationSecurityMetadataSourceService;
    @Autowired
    private MyAccessDecisionManager myAccessDecisionManager;


    //过滤器的配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor() {
                    @Override
                    public  O postProcess(O o) {
                        o.setSecurityMetadataSource(myInvocationSecurityMetadataSourceService);
                        o.setAccessDecisionManager(myAccessDecisionManager);
                        return o;
                    }
                })
                .antMatchers("/","/login","/index")// 允许直接访问的地址
                .permitAll()
                .anyRequest().authenticated()   // 其他地址的访问均需验证权限
                .and()
                .formLogin()
                .loginPage("/login")   //  登录页
                .successForwardUrl("/index")  // 成功后跳转页面
                .permitAll()
                .and()
                .logout()
                .logoutSuccessUrl("/index");
    }


    //AuthenticatManager的配置
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {

        return new PasswordEncoderImpl();
    }
}

    这个文件注入了4个自定义的实现类:

    MyUserDetailsService  -- 登录认证类

    MyInvocationSecurityMetadataSourceService -- 提供访问uri所需要的权限

    MyAccessDecisionManager -- 权限校验逻辑

    PasswordEncoderImpl -- 加密规则,SpringSecurity默认不支持md5加密,这里自定义一个md5加密

 

    security文件夹下就是这些自定义的类,分别贴出代码

    MyUserDetailsService  -- SpringSecurity的登录认证使用UserDetailsService,这个类就实现了UserDetailsService接口,重写逻辑

,实现代码如下

/**
 * @program: springcloud
 * @description: 自定义的UserDetailsService
 * @author: paddy
 * @create: 2019-04-15 12:47
 **/
@Service
public class MyUserDetailsService implements UserDetailsService {


    @Autowired
    private ISysUserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserVo userVo = userService.selectVoByLoginName(username); // 查询用户
        if (userVo == null ) { // 如果没查询到返回错误
            throw new UsernameNotFoundException("用户名称错误!");
        }
        List authorities = new ArrayList<>();
        Iterator iRole = userVo.getRolesList().iterator();
        while (iRole.hasNext()){ // 添加查询到的角色信息
            authorities.add(new SimpleGrantedAuthority(iRole.next().getName()));
        }
        return new User(userVo.getLoginName(), userVo.getPassword(),authorities);
    }
}

    这里查询出用户信息,把查询出的UserVo存到SpringSecurity提供的User类里,User的参数分别为用户名,密码,角色信息。然后SpringSecurity会帮我们用这个User里的用户名和密码和前台传过来的用户名密码做比对。

    PasswordEncoderImpl 这个没什么好说的,就是自己实现md5加密,贴代码:

/**
 * @program: springcloud
 * @description: 自定义的密码处理逻辑,采用md5加密
 * @author: paddy
 * @create: 2019-04-15 12:46
 **/
public class PasswordEncoderImpl implements PasswordEncoder {

    @Override
    public String encode(CharSequence charSequence) {
        try {
            // MD5加密密码
            return DigestUtils.md5Hex(charSequence.toString().getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        return encode(charSequence).equals(s);
    }
}

    MyInvocationSecurityMetadataSourceService -- SpringSecurity的权限数据使用FilterInvocationSecurityMetadataSource,这个类实现了FilterInvocationSecurityMetadataSource接口,在这里重写逻辑

/**
 * @program: springcloud
 * @description: 自定义的Metadata,通过当前请求地址,获取该地址所需要的角色
 * @author: paddy
 * @create: 2019-04-15 12:56
 **/
@Service
public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {

    @Autowired
    ISysResourceService resourceService;

    @Override
    public Collection getAttributes(Object o) throws IllegalArgumentException {

        //object 中包含用户请求的request 信息
        HttpServletRequest request = ((FilterInvocation) o).getHttpRequest();
        String uri = request.getRequestURI();
        List roleNameList = resourceService.selectRoleNamesByURI(uri); // 查出资源所需要的角色
        if(roleNameList!=null){
            List attributes = new ArrayList();
            for(String roleName:roleNameList){
                attributes.add(new SecurityConfig(roleName));
            }
            return attributes;
        }
        return null;
    }

    @Override
    public Collection getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class aClass) {
        return true;
    }
}

    逻辑很简单,通过访问的uri查询数据库,得到访问某个uri所需要的权限信息

    MyAccessDecisionManager -- SpringSecurity的权限数据使用AccessDecisionManager,这个类实现了AccessDecisionManager接口,在这里重写逻辑

/**
 * @program: springcloud
 * @description: 自定义的AccessDecisionManager,用来判断权限
 * @author: paddy
 * @create: 2019-04-15 14:39
 **/
@Service
public class MyAccessDecisionManager implements AccessDecisionManager {

    /**
     *
     * @param authentication 当前用户所具有的权限
     * @param o
     * @param configAttributes  当前访问所需要的权限
     * @throws AccessDeniedException
     * @throws InsufficientAuthenticationException
     */
    @Override
    public void decide(Authentication authentication, Object o, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        Iterator iterator = configAttributes.iterator();
        while (iterator.hasNext()) {
            ConfigAttribute ca = iterator.next();
            //当前请求需要的权限
            String needRole = ca.getAttribute();
            //当前用户所具有的权限
            Collection authorities = authentication.getAuthorities();
            for (GrantedAuthority authority : authorities) {
                if (authority.getAuthority().equals(needRole)) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("权限不足!");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    @Override
    public boolean supports(Class aClass) {
        return true;
    }
}

    这个类的注释写的很清楚,通过上一个自定义接口MyInvocationSecurityMetadataSourceService 获取uri所需要的角色,然后在和已登录的用户信息里存的角色比对。

    

转载于:https://my.oschina.net/u/3244751/blog/3037106

你可能感兴趣的:(SpringBoot使用SpringSecurity自定义用户登录与自定义权限-session篇)