SpringSecurity深度解析与实践(3)

发布时间:2023年12月24日


在这里插入图片描述

引言

SpringSecurity深度解析与实践(2)的网址

SpringSecurity之授权

授权介绍

Spring Security 中的授权分为两种类型:

  • 基于角色的授权:以用户所属角色为基础进行授权,如管理员、普通用户等,通过为用户分配角色来控制其对资源的访问权限。
  • 基于资源的授权:以资源为基础进行授权,如 URL、方法等,通过定义资源所需的权限,来控制对该资源的访问权限。

Spring Security 提供了多种实现授权的机制,最常用的是使用基于注解的方式,建立起访问资源和权限之间的映射关系。

其中最常用的两个注解是 @Secured@PreAuthorize@Secured 注解是更早的注解,基于角色的授权比较适用,@PreAuthorize 基于 SpEL 表达式的方式,可灵活定义所需的权限,通常用于基于资源的授权。

java权限集成

WebSecurityConfig配置类

package com.yuan.springsecurity1.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yuan.springsecurity1.resp.JsonResponseBody;
import com.yuan.springsecurity1.resp.JsonResponseStatus;
import com.yuan.springsecurity1.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

@Configuration
//开启SpringSecurity的默认行为
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Autowired
    private DataSource dataSource;

    /**
     * 配置持久化Token方式,注意tokenRepository.setCreateTableOnStartup()配置
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        // 设置为true要保障数据库该表不存在,不然会报异常哦
        // 所以第二次打开服务器应用程序的时候得把它设为false
        tokenRepository.setCreateTableOnStartup(false);
        return tokenRepository;
    }

        @Autowired
        private MyUserDetailsService myUserDetailsService;

        @Autowired
        ObjectMapper objectMapper;

        @Autowired
        MyAuthenticationFailureHandler myAuthenticationFailureHandler;


    /**
     * 获取AuthenticationManager(认证管理器),登录时认证使用(基于数据库方式)
     * @param
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        //创建DaoAuthenticationProvider
        DaoAuthenticationProvider provider=new DaoAuthenticationProvider();
        //设置userDetailsService,基于数据库方式进行身份认证
        provider.setUserDetailsService(myUserDetailsService);
        //配置密码编码器
        provider.setPasswordEncoder(passwordEncoder());
        return new ProviderManager(provider);
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http)
            throws Exception{
        http.authorizeRequests()
                    // 开放接口访问权限,不需要登录就可以访问
                    .antMatchers("/toLogin").permitAll()
                    //访问路径有admin的方法时,需要有ADMIN的身份
//                    .antMatchers("/admin/**").hasRole("ADMIN")
//                    .antMatchers("/user/**").hasAnyRole("ADMIN","USER")
                    // 其余所有请求全部需要鉴权认证
                    .anyRequest().authenticated()
                .and()
                .formLogin()
                // 设置登录页面的 URL
                .loginPage("/toLogin")
                // 设置登录请求的 URL,即表单提交的 URL
                .loginProcessingUrl("/userLogin")
                // 设置登录表单中用户名字段的参数名,默认为username
                .usernameParameter("username")
                // 设置登录表单中密码字段的参数名,默认为password
                .passwordParameter("password")
                //成功后的处理
                .successHandler((req,resp,auth)->{
//                    resp.sendRedirect("/index");
                    Object user = auth.getPrincipal();
                    objectMapper.writeValue(resp.getOutputStream(), JsonResponseBody.success(user));
                })
                //登录失败的处理
                .failureHandler(myAuthenticationFailureHandler)
                .and()
                .exceptionHandling()
//                .accessDeniedPage("/noAccess")
                //权限不够处理
                .accessDeniedHandler((req,resp,ex)->{
                    objectMapper.writeValue(resp.getOutputStream(),JsonResponseBody.other(JsonResponseStatus.NO_ACCESS));
                })
                //没有认证(登录)处理
                .authenticationEntryPoint((req,resp,ex)->{
                    objectMapper.writeValue(resp.getOutputStream(),JsonResponseBody.other(JsonResponseStatus.NO_LOGIN));
                })
                .and()
                .logout()
                // 设置安全退出的URL路径
                .logoutUrl("/logout")
                // 设置退出成功后跳转的路径
                .logoutSuccessUrl("/toLogin")
                .and()
                .rememberMe()
                // 指定 rememberMe 的参数名,用于在表单中携带 rememberMe 的值。
                .rememberMeParameter("remember-me")
                // 指定 rememberMe 的有效期,单位为秒,默认2周。
                .tokenValiditySeconds(600)
                // 指定 rememberMe 的 cookie 名称。
                .rememberMeCookieName("remember-me-cookie")
                // 指定 rememberMe 的 token 存储方式,可以使用默认的 PersistentTokenRepository 或自定义的实现。
                .tokenRepository(persistentTokenRepository())
                // 指定 rememberMe 的认证方式,需要实现 UserDetailsService 接口,并在其中查询用户信息。
                .userDetailsService(myUserDetailsService);
//        http.csrf().disable();
    	return http.build();
    }
}

登录权限绑定

package com.yuan.springsecurity1.config;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.yuan.springsecurity1.pojo.*;
import com.yuan.springsecurity1.mapper.UserMapper;
import com.yuan.springsecurity1.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.Component;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author yuan
 * @since 2023-12-21
 */
@Component
public class MyUserDetailsService implements  UserDetailsService {

    @Autowired
    private IUserService iUserService;

    @Autowired
    private IUserRoleService iUserRoleService;

    @Autowired
    private IRoleService iRoleService;

    @Autowired
    private IRoleModuleService iRoleModuleService;

    @Autowired
    private IModuleService iModuleService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = iUserService.getOne(new QueryWrapper<User>().eq("username", username));
        if(user==null)
            throw new UsernameNotFoundException("用户名无效");
        //查询所有的身份,map 遍历数据对象,返回的数据放到一个新的流中,collect(Collectors.toList())用list集合接收
        List<Integer> userIds = iUserRoleService.list(new QueryWrapper<UserRole>().eq("user_id", user.getId())).stream().map(UserRole::getRoleId)
                .collect(Collectors.toList());
        //根据role_id拿到身份名称集合
        List<String> roleNames = iRoleService.list(new QueryWrapper<Role>().in("role_id", userIds)).stream().map(Role::getRoleName).collect(Collectors.toList());
        //根据身份id role_id拿到权限id module_id
        List<Integer> moduleIds = iRoleModuleService.list(new QueryWrapper<RoleModule>().in("role_id", userIds)).stream().map(RoleModule::getModuleId).collect(Collectors.toList());
        //根据权限id拿到对应的url
        List<String> urls = iModuleService.list(new QueryWrapper<Module>().in("id", moduleIds)).stream().map(Module::getUrl).filter(Objects::nonNull).collect(Collectors.toList());

        roleNames.addAll(urls);
        List<SimpleGrantedAuthority> authorities = roleNames.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
        user.setAuthorities(authorities);
        return user;

    }
}

Controller类数据

    @ResponseBody
    @RequestMapping("/o_f")
    @PreAuthorize("hasAnyAuthority('order:manager:analysis')")
    public String o_f() {
        return "订单分析";
    }

    @ResponseBody
    @RequestMapping("/b_add")
    @PreAuthorize("hasAnyAuthority('book:manager:add')")
    public String b_add() {
        return "书籍新增";
    }

需要添加一个规则,判断是否有权限,我这个放在WebSecurityConfig类上

@EnableGlobalMethodSecurity(prePostEnabled = true)

登录失败三次用户上锁

搭配WebSecurityConfig类中失败的对象

package com.yuan.springsecurity1.config;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yuan.springsecurity1.pojo.User;
import com.yuan.springsecurity1.resp.JsonResponseBody;
import com.yuan.springsecurity1.resp.JsonResponseStatus;
import com.yuan.springsecurity1.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author 叶秋
 * @site
 * @company 卓京公司
 * @create 2023-12-23 23:21
 */
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Autowired
    ObjectMapper objectMapper;
    @Autowired
    private IUserService iUserService;
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {

        if(1==2){ //假设超过三次失败
           User user = iUserService.getOne(new QueryWrapper<User>().eq("username", request.getParameter("username")));
            user.setAccountNonLocked(false);
            iUserService.updateById(user);
       }
        objectMapper.writeValue(response.getOutputStream(), JsonResponseBody.other(JsonResponseStatus.LOGIN_FAILURE));
    }
}

文章来源:https://blog.csdn.net/2201_75869073/article/details/135176608
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。