SpringSecurity【3】之授权

继续昨天的认证,今天来分析
在Spring Security中,授权是指对用户访问系统资源的限制。Spring Security提供了多种授权方式,包括基于角色的授权、基于表达式的授权、注解授权等。

基于角色的授权是指通过为用户分配不同的角色来限制其访问系统资源。Spring Security提供了一些默认的角色,如ROLE_USER和ROLE_ADMIN等,也支持开发者自定义角色。在Spring Security中,我们可以使用标签<intercept-url>和<http>来配置基于角色的授权。下面是一个例子:

复制代码
<http> <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" /> </http>

上述配置表示只有拥有ROLE_ADMIN角色的用户才能访问/admin/**下的资源。

Spring Security还提供了注解授权、方法级授权等多种授权方式,可以根据具体需求进行选择和配置。

分析前端传来的值:

访问:得到了json的对象

(关于权限:就是往数据库中放字段,设置了就有)

通过流的视图方式查看:

代码:

复制代码
MyUserDtealsService
复制代码
package com.lya.securty.config;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lya.securty.pojo.*;
import com.lya.securty.service.IModuleService;
import com.lya.securty.service.IRoleModuleService;
import com.lya.securty.service.IUserRoleService;
import com.lya.securty.service.IUserService;
import com.lya.securty.service.impl.ModuleServiceImpl;
import com.lya.securty.service.impl.RoleModuleServiceImpl;
import com.lya.securty.service.impl.RoleServiceImpl;
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 java.util.List;
import java.util.stream.Collectors;

@Component
//把他编程一个组件
public class MyUserDtealsService implements UserDetailsService {

    @Autowired
    private IUserService userService;

    @Autowired
    private IUserRoleService iUserRoleService;

    @Autowired
    private ModuleServiceImpl moduleService;

    @Autowired
    private RoleServiceImpl roleService;

    @Autowired
    private RoleModuleServiceImpl roleModuleService;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.getOne(new QueryWrapper<User>().eq("username", username));
        if (user == null) {
            throw new UsernameNotFoundException("用户无效");
        }
//        1.查出所有的身份, map遍历,返回新数据-->流,将流编程list
//        2.多个id对应的权限,连接权限表

        List<Integer> role_ids = iUserRoleService
//        查出所有的身份
                .list(new QueryWrapper<UserRole>().eq("user_id", user.getId()))
//                .stream().map(r->r.getUserId());
//        返回新数据-->流
                .stream().map(UserRole::getUserId)
//                将流编程list
                .collect(Collectors.toList());
//         用id查询身份对一的名字---比如1,2普通用户超级管理。
        List<String> roles = roleService.list(new QueryWrapper<Role>().in("role_id", role_ids))
                //        返回新数据-->流
                .stream().map(Role::getRoleName)
//                将流编程list
                .collect(Collectors.toList());

//        根据身份id查询权限
        List<Integer> module_ids = roleModuleService.list(new QueryWrapper<RoleModule>().in("role_id", role_ids))
//        返回新数据-->流
                .stream().map(RoleModule::getModuleId)
//                将流编程list
                .collect(Collectors.toList());
//      根据权限id查询权限

        List<String> moudelse = moduleService.list(new QueryWrapper<Module>().in("id", module_ids))
                //        返回新数据-->流
                .stream().map(Module::getUrl)
//                .filter(Object::nonNull)
//                将流编程list
                .collect(Collectors.toList());

//        roles[权限]
//        modules 访问url
        roles.addAll(moudelse);

        List<SimpleGrantedAuthority> authorities = roles.stream().map(e -> {
            return new SimpleGrantedAuthority(e);
        }).collect(Collectors.toList());

        user.setAuthorities(authorities);
        return user;
    }


}
复制代码
WebSecurityConfig
复制代码
package com.lya.securty.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lya.securty.resp.JsonResponseBody;
import com.lya.securty.resp.JsonResponseStatus;
import com.lya.securty.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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.sql.DataSource;

/**
 * @author all
 */
@Configuration//启动配置类 spring进行管理不然加载不了这个类
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
//开启这个类
public class WebSecurityConfig {

@Autowired
private DataSource dataSource;

//SpringBoot自己帶的一個序列化的类
    @Autowired
    private ObjectMapper objectMapper;
    /**
     * 配置持久化Token方式,注意tokenRepository.setCreateTableOnStartup()配置
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        // 设置为true要保障数据库该表不存在,不然会报异常哦
        // 所以第二次打开服务器应用程序的时候得把它设为false
        tokenRepository.setCreateTableOnStartup(false);
        return tokenRepository;
    }
//    加密类
    @Bean
//    @Primary
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    /**
     * 配置密码编码器,首次采用明文密码方式进行比对校验
     */
//    @Bean
//    public PasswordEncoder passwordEncoder(){
//        return NoOpPasswordEncoder.getInstance();
//    }
    @Autowired
    private UserServiceImpl userService;

    @Autowired
    private MyUserDtealsService myUserDtealsService;
//    @Bean
//    public UserDetailsService userDetailsService() {
//        UserDetails admin = User.withUsername("admin")
//                .password(bcryptPasswordEncoder().encode("123456"))
//                .roles("ADMIN", "USER").build();//权限
//        UserDetails user = User.withUsername("user")
//                .password(bcryptPasswordEncoder().encode("123456"))
//                .roles("USER").build();
//        return new InMemoryUserDetailsManager(admin, user);
//    }

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

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        //认证请求
        http.authorizeRequests()
                //antMatchers匹配对应的路径
                //permitAll允许访问
                .antMatchers("/toLogin").permitAll()
                //hasRole具备身份
                //hasAnyRole具备多个身份
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasAnyRole("ADMIN", "USER")
               //anyRequest其余所有请求
                .anyRequest()
                //authenticated认证
                .authenticated()
                .and()
                .formLogin()
                //当前登录页面
                .loginPage("/toLogin")
                //设置处理登录请求的接口
                .loginProcessingUrl("/userLogin")
                //用户的数据参数
                .usernameParameter("username")
                .passwordParameter("password")
                //转发  进入首页  地址栏不改变
                //.successForwardUrl("/index")
                //成功处理器
                .successHandler((req,resp,auth)->{
                    Object user = auth.getPrincipal();
                    JsonResponseBody.success(user);
                    //重定向  跳转首页
//                    resp.sendRedirect("/index");
//                    这里不要直接跳,应为适应前后端分离
//                    (通过流的方式,响应数据到前端)
                    objectMapper
                            .writeValue(resp.getOutputStream(),JsonResponseBody.success(user));
                })
                //失败处理器
                .failureHandler((req,resp,ex)->{
                    //错误信息提示
                    req.setAttribute("msg",ex.getMessage());
                    //重定向  跳转登录
                    req.getRequestDispatcher("/toLogin").forward(req,resp);
                })
                .and()
                .exceptionHandling()
//                .accessDeniedPage((req,resp,ex)->{
//                    //错误信息提示无权限
//                    objectMapper
//                            .writeValue(resp.getOutputStream(),JsonResponseBody.other(JsonResponseStatus.NO_LOGIN));
//                })

//              五登录
                .authenticationEntryPoint((req,resp,ex)->{
                    objectMapper
                            .writeValue(resp.getOutputStream(),JsonResponseBody.other(JsonResponseStatus.NO_LOGIN));
                })
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/")
                .and()
                .rememberMe()
                // 指定 rememberMe 的参数名,用于在表单中携带 rememberMe 的值。
                .rememberMeParameter("remember-me")
                // 指定 rememberMe 的有效期,单位为秒,默认2周。
                .tokenValiditySeconds(300)
                // 指定 rememberMe 的 cookie 名称。
                .rememberMeCookieName("remember-me-cookie")
                // 指定 rememberMe 的 token 存储方式,可以使用默认的 PersistentTokenRepository 或自定义的实现。
                .tokenRepository(persistentTokenRepository())
                // 指定 rememberMe 的认证方式,需要实现 UserDetailsService 接口,并在其中查询用户信息。
                .userDetailsService(userService);
//        防禦
        http.csrf().disable();
        http.exceptionHandling().accessDeniedPage("/noAccess");
        return http.build();
    }

}
复制代码
UserController(添加两个具有访问权限的)
复制代码
package com.lya.securty.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author all
 */
@Controller
public class UserController {

    @RequestMapping("/toLogin")
    public String toLogin() {
        return "login";
    }

    @RequestMapping("/userLogin")
    public String userLogin(String username, String password) {
        System.out.println("username=" + username + ",password=" + password);
        return "index";
    }

    @RequestMapping("/admin/toAddUser")
    public String toAddUser() {
        return "admin/addUser";
    }

    @RequestMapping("/admin/toListUser")
    public String toListUser() {
        return "admin/listUser";
    }

    @RequestMapping("/admin/toResetPwd")
    public String toResetPwd() {
        return "admin/resetPwd";
    }

    @RequestMapping("/admin/toUpdateUser")
    public String toUpdateUser() {
        return "admin/updateUser";
    }

    @RequestMapping("/user/toUpdatePwd")
    public String toUpdatePwd() {
        return "user/updatePwd";
    }

    @RequestMapping("/index")
    public String index(){
        return "index";
    }

    @ResponseBody
    @RequestMapping("/add")
    @PreAuthorize("hasAuthority('book:manager:add')")
    public String add() {
        return "订单新增";
    }

    @ResponseBody
    @RequestMapping("/oradd")
    @PreAuthorize("hasAuthority('order:manager:add')")

    public String oradd() {
        return "订单新增";
    }

    @RequestMapping("/noAccess")
    public String noAccess() {
        return "accessDenied";
    }

}
复制代码
JsonResponseBody(返回前端的Json格式数据)
复制代码
package com.lya.securty.resp;

import lombok.Data;

@Data
public class JsonResponseBody<T> {

    private Integer code;
    private String msg;
    private T data;
    private Long total;

    private JsonResponseBody(JsonResponseStatus jsonResponseStatus, T data) {
        this.code = jsonResponseStatus.getCode();
        this.msg = jsonResponseStatus.getMsg();
        this.data = data;
    }

    private JsonResponseBody(JsonResponseStatus jsonResponseStatus, T data, Long total) {
        this.code = jsonResponseStatus.getCode();
        this.msg = jsonResponseStatus.getMsg();
        this.data = data;
        this.total = total;
    }

    public static <T> JsonResponseBody<T> success() {
        return new JsonResponseBody<T>(JsonResponseStatus.OK, null);
    }

    public static <T> JsonResponseBody<T> success(T data) {
        return new JsonResponseBody<T>(JsonResponseStatus.OK, data);
    }

    public static <T> JsonResponseBody<T> success(T data, Long total) {
        return new JsonResponseBody<T>(JsonResponseStatus.OK, data, total);
    }

    public static <T> JsonResponseBody<T> unknown() {
        return new JsonResponseBody<T>(JsonResponseStatus.UN_KNOWN, null);
    }

    public static <T> JsonResponseBody<T> other(JsonResponseStatus jsonResponseStatus) {
        return new JsonResponseBody<T>(jsonResponseStatus, null);
    }

}

响应前端的msg

复制代码
package com.lya.securty.resp;

import lombok.Getter;

@Getter
public enum JsonResponseStatus {

    OK(200, "OK"),
    UN_KNOWN(500, "未知错误"),
    RESULT_EMPTY(1000, "查询结果为空"),
    NO_ACCESS(3001, "没有权限"),
    NO_LOGIN(4001, "没有登录"),
    LOGIN_FAILURE(5001, "登录失败"),
    ;

    private final Integer code;
    private final String msg;

    JsonResponseStatus(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

}
相关推荐
JH30736 小时前
SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案
java·spring boot·spring
qq_12498707539 小时前
基于SSM的动物保护系统的设计与实现(源码+论文+部署+安装)
java·数据库·spring boot·毕业设计·ssm·计算机毕业设计
Coder_Boy_10 小时前
基于SpringAI的在线考试系统-考试系统开发流程案例
java·数据库·人工智能·spring boot·后端
2301_8187320610 小时前
前端调用控制层接口,进不去,报错415,类型不匹配
java·spring boot·spring·tomcat·intellij-idea
码字的字节10 小时前
Spring Cloud服务注册与发现(一):手把手搭建Eureka Server,详解高可用配置
spring·spring cloud·eureka
大厂资深架构师10 小时前
Spring Cloud Eureka在后端系统中的服务剔除策略
spring·spring cloud·ai·eureka
汤姆yu13 小时前
基于springboot的尿毒症健康管理系统
java·spring boot·后端
暮色妖娆丶13 小时前
Spring 源码分析 单例 Bean 的创建过程
spring boot·后端·spring
biyezuopinvip14 小时前
基于Spring Boot的企业网盘的设计与实现(任务书)
java·spring boot·后端·vue·ssm·任务书·企业网盘的设计与实现