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;
    }

}
相关推荐
初晴~4 小时前
【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
java·数据库·redis·分布式·后端·spring·
黑胡子大叔的小屋5 小时前
基于springboot的海洋知识服务平台的设计与实现
java·spring boot·毕业设计
计算机毕设孵化场5 小时前
计算机毕设-基于springboot的校园社交平台的设计与实现(附源码+lw+ppt+开题报告)
spring boot·课程设计·计算机毕设论文·计算机毕设ppt·计算机毕业设计选题推荐·计算机选题推荐·校园社交平台
雷神乐乐6 小时前
Spring学习(一)——Sping-XML
java·学习·spring
苹果醋36 小时前
Golang的文件加密工具
运维·vue.js·spring boot·nginx·课程设计
小林coding6 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
文大。7 小时前
2024年广西职工职业技能大赛-Spring
java·spring·网络安全
小马爱打代码8 小时前
Spring Boot 中 Map 的最佳实践
java·spring boot·spring
全栈开发帅帅8 小时前
基于springboot+vue实现的博物馆游客预约系统 (源码+L文+ppt)4-127
java·spring boot·后端
m0_748255659 小时前
Springboot基于Web的景区疫情预警系统设计与实现5170q(程序+源码+数据库+调试部署+开发环境)
前端·数据库·spring boot