spring boot中常用的安全框架 Security框架 利用Security框架实现用户登录验证token和用户授权(接口权限控制)

这篇具有很好参考价值的文章主要介绍了spring boot中常用的安全框架 Security框架 利用Security框架实现用户登录验证token和用户授权(接口权限控制)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

spring boot中常用的安全框架
Security 和 Shiro 框架

Security 两大核心功能 认证 和 授权
重量级

Shiro 轻量级框架 不限于web 开发

在不使用安全框架的时候
一般我们利用过滤器和 aop自己实现 权限验证 用户登录

Security 实现逻辑

  1. 输入用户名和密码 提交
  2. 把提交用户名和密码封装对象
    3、4 调用方法实现验证
    5、调用方法、根据用户米查询用户信息
    6、查询用户信息返回对象
    7、密码比较
    8、填充回、返回
    9、返回对象放到上下文对象里面
    引入依赖
     <!-- Spring Security依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

刚开始测试的话 默认密码在控制台

把Security框架 使用到自己项目中
具体核心组件

- 第一步、登录接口 判断用户名和密码

自定义以下组件
1、 创建自己相对应的User 实体类 继承 org.springframework.security.core.userdetails.User
在里面定义自己的实体类字段和实现一个CustomUser()方法

package com.oa.security.custom;


import com.oa.model.system.SysUser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

import java.util.Collection;

public class CustomUser extends User {

    /**
     * 我们自己的用户实体对象,要调取用户信息时直接获取这个实体对象。(这里我就不写get/set方法了)
     */
    private SysUser sysUser;

    public CustomUser(SysUser sysUser, Collection<? extends GrantedAuthority> authorities) {
        super(sysUser.getUsername(), sysUser.getPassword(), authorities);
        this.sysUser = sysUser;
    }

    public SysUser getSysUser() {
        return sysUser;
    }

    public void setSysUser(SysUser sysUser) {
        this.sysUser = sysUser;
    }
}

2、 重写 loadUserByUsername 方法
自定义一个 UserDetailsService 接口
继承org.springframework.security.core.userdetails.UserDetailsService 下的这个类
重写 UserDetailsService里的 loadUserByUsername方法
自定义一个 UserDetailsService 接口 的具体实现类 就是去数据库验证的实现类 比如 UserDetailsServiceImpl
在这个类 实现 loadUserByUsername 方法
实现后 最后返回 第一步创建的自定义的User 实体类
因为自己自定义的User类继承了UserDetails类
所以等于把数据交给了Security框架

UserDetailsService 接口

package com.oa.security.custom;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

@Component
public interface UserDetailsService extends org.springframework.security.core.userdetails.UserDetailsService {

    /**
     * 根据用户名获取用户对象(获取不到直接抛异常)
     */
    @Override
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

UserDetailsServiceImpl实现接口

package com.oa.auth.service.impl;


import com.oa.auth.service.SysMenuService;
import com.oa.auth.service.SysUserService;
import com.oa.model.system.SysUser;
import com.oa.security.custom.CustomUser;
import org.springframework.beans.factory.annotation.Autowired;
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.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private SysUserService sysUserService;
    
    @Autowired
    private SysMenuService sysMenuService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名进行查询
        SysUser sysUser = sysUserService.getUserByUserName(username);
        if(null == sysUser) {
            throw new UsernameNotFoundException("用户名不存在!");
        }

        if(sysUser.getStatus().intValue() == 0) {
            throw new RuntimeException("账号已停用");
        }

        //根据userid查询用户操作权限数据
        List<String> userPermsList = sysMenuService.findUserPermsByUserId(sysUser.getId());
        //创建list集合,封装最终权限数据
        List<SimpleGrantedAuthority> authList = new ArrayList<>();
        //查询list集合遍历
        for (String perm : userPermsList) {
            authList.add(new SimpleGrantedAuthority(perm.trim()));
        }
       // return null;
        return new CustomUser(sysUser, authList);
    }
}

3、 自定义一个秘密校验器 CustomMd5PasswordEncoder 实现 org.springframework.security.crypto.password.PasswordEncoder 接口

package com.oa.security.custom;


import com.oa.common.utils.MD5;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

@Component
public class CustomMd5PasswordEncoder implements PasswordEncoder {
    public String encode(CharSequence rawPassword) {
        return MD5.encrypt(rawPassword.toString());
    }

    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return encodedPassword.equals(MD5.encrypt(rawPassword.toString()));
    }
}

4、 创建一个过滤器 来验证token 比如 TokenLoginFilter
继承 org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
在方法里 定义四个方法 都是重写父类方法
定义一个构造方法
登录认证方法 获取输入的用户名和密码,调用方法认证 attemptAuthentication() 进行账号密码认证 认证实际就是执行了我们设置的第一步的内容
认证成功调用方法 successfulAuthentication() 如果认证成功 这个方法里 就处理比如生成token 存入权限 等
认证失败调用方法 unsuccessfulAuthentication() 如果认证失败 这个方法里 就处理失败的逻辑

package com.oa.security.filter;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.oa.common.jwt.JwtHelper;
import com.oa.common.result.ResponseUtil;
import com.oa.common.result.Result;
import com.oa.common.result.ResultCodeEnum;
import com.oa.security.custom.CustomUser;
import com.oa.vo.system.LoginVo;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {

    private RedisTemplate redisTemplate;
    //构造方法
    public TokenLoginFilter(AuthenticationManager authenticationManager,
                            RedisTemplate redisTemplate) {
        this.setAuthenticationManager(authenticationManager);
        this.setPostOnly(false);
        //指定登录接口及提交方式,可以指定任意路径 安全框架会从这个接口里取相应数据
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/system/index/login","POST"));
        this.redisTemplate = redisTemplate;
    }

    //登录认证
    //获取输入的用户名和密码,调用方法认证
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response)
            throws AuthenticationException {
        try {
            //获取用户信息  实际就是获取的登录接口的 那个实体类里的数据
            LoginVo loginVo = new ObjectMapper().readValue(request.getInputStream(), LoginVo.class);
            //封装对象 然后把用户名和密码 传入进去
            Authentication authenticationToken =
                    new UsernamePasswordAuthenticationToken(loginVo.getUsername(), loginVo.getPassword());
            //调用方法
            return this.getAuthenticationManager().authenticate(authenticationToken);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //认证成功调用方法
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication auth)
            throws IOException, ServletException {
        //获取当前用户
        CustomUser customUser = (CustomUser)auth.getPrincipal();
        //生成token
        String token = JwtHelper.createToken(customUser.getSysUser().getId(),
                customUser.getSysUser().getUsername());

        //获取当前用户权限数据,放到Redis里面 key:username   value:权限数据
        redisTemplate.opsForValue().set(customUser.getUsername(),
                JSON.toJSONString(customUser.getAuthorities()));

        //返回
        Map<String,Object> map = new HashMap<>();
        map.put("token",token);
        ResponseUtil.out(response, Result.ok(map));
    }

    //认证失败调用方法
    protected void unsuccessfulAuthentication(HttpServletRequest request,
                                              HttpServletResponse response,
                                              AuthenticationException failed)
            throws IOException, ServletException {
        ResponseUtil.out(response,Result.build(null, ResultCodeEnum.LOGIN_ERROR));
    }
}

以上就是用户调用登录接口 利用Security框架 完成登录

第二步、认证解析token组件:解决调用其他接口时 看看用户有没有登录
判断请求头是否有token 如果有,认证完成 (通俗一点就是 判断当前是否登录)
自定义一个 TokenAuthenticationFilter 继承 org.springframework.web.filter.OncePerRequestFilter;
重写 里面的方法 doFilterInternal()

package com.oa.security.filter;

import com.alibaba.fastjson.JSON;
import com.oa.common.jwt.JwtHelper;
import com.oa.common.result.ResponseUtil;
import com.oa.common.result.Result;
import com.oa.common.result.ResultCodeEnum;
import com.oa.security.custom.LoginUserInfoHelper;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class TokenAuthenticationFilter extends OncePerRequestFilter {

    private RedisTemplate redisTemplate;

    public TokenAuthenticationFilter(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {
        //如果是登录接口,直接放行
        if("/admin/system/index/login".equals(request.getRequestURI())) {
            chain.doFilter(request, response);
            return;
        }

        UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
        if(null != authentication) {
            SecurityContextHolder.getContext().setAuthentication(authentication);
            chain.doFilter(request, response);
        } else {
            ResponseUtil.out(response, Result.build(null, ResultCodeEnum.LOGIN_ERROR));
        }
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        //请求头是否有token
        String token = request.getHeader("token");
        if(!StringUtils.isEmpty(token)) {
            String username = JwtHelper.getUsername(token);
            if(!StringUtils.isEmpty(username)) {
                //当前用户信息放到ThreadLocal里面
                LoginUserInfoHelper.setUserId(JwtHelper.getUserId(token));
                LoginUserInfoHelper.setUsername(username);

                //通过username从redis获取权限数据
                String authString = (String)redisTemplate.opsForValue().get(username);
                //把redis获取字符串权限数据转换要求集合类型 List<SimpleGrantedAuthority>
                if(!StringUtils.isEmpty(authString)) {
                    List<Map> maplist = JSON.parseArray(authString, Map.class);
                    System.out.println(maplist);
                    List<SimpleGrantedAuthority> authList = new ArrayList<>();
                    for (Map map:maplist) {
                        String authority = (String)map.get("authority");
                        authList.add(new SimpleGrantedAuthority(authority));
                    }
                    return new UsernamePasswordAuthenticationToken(username,null, authList);
                } else {
                    return new UsernamePasswordAuthenticationToken(username,null, new ArrayList<>());
                }
            }
        }
        return null;
    }
}

第三步、在配置类配置相关认证类

package com.oa.security.config;

import com.oa.security.custom.CustomMd5PasswordEncoder;
import com.oa.security.filter.TokenAuthenticationFilter;
import com.oa.security.filter.TokenLoginFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

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

    @Autowired
    private RedisTemplate redisTemplate;

    //导入这个接口的时候 有可能会报错找不到这个类
    //我们有自己创建了一个 UserDetailsService 接口
    // 如果导入我们的 也会报错
    // 解决方案就是 把我们自定义的 UserDetailsService 接口 继承 框架下的UserDetailsService
    // 就可以了
    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private CustomMd5PasswordEncoder customMd5PasswordEncoder;

    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 这是配置的关键,决定哪些接口开启防护,哪些接口绕过防护
        http
                //关闭csrf跨站请求伪造
                .csrf().disable()
                // 开启跨域以便前端调用接口
                .cors().and()
                .authorizeRequests()
                // 指定某些接口不需要通过验证即可访问。登陆接口肯定是不需要认证的
                //.antMatchers("/admin/system/index/login").permitAll()
                // 这里意思是其它所有接口需要认证才能访问
                .anyRequest().authenticated()
                .and()
                //TokenAuthenticationFilter放到UsernamePasswordAuthenticationFilter的前面,这样做就是为了除了登录的时候去查询数据库外,其他时候都用token进行认证。
                .addFilterBefore(new TokenAuthenticationFilter(redisTemplate),
                        UsernamePasswordAuthenticationFilter.class)
                .addFilter(new TokenLoginFilter(authenticationManager(),redisTemplate));

        //禁用session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 指定UserDetailService和加密器
        auth.userDetailsService(userDetailsService).passwordEncoder(customMd5PasswordEncoder);
    }

    /**
     * 配置哪些请求不拦截
     * 排除swagger相关请求
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/admin/modeler/**","/diagram-viewer/**","/editor-app/**","/*.html",
                "/admin/processImage/**",
                "/admin/wechat/authorize","/admin/wechat/userInfo","/admin/wechat/bindPhone",
                "/favicon.ico","/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**", "/doc.html");
    }
}

用户授权

代码在上面的类里已经存在,这里只截图提现
比如按钮权限 哪些按钮课余访问
这些按钮权限 在 数据库存着 一般是给前端使用
但是这种操作 如果懂技术的人 可以绕过前端 直接访问后端api接口
为了解决这个问题 所以后端也需要做用户授权
第一步 在查询用户名密码验证的时候 把用户的按钮权限查询出来
一个按钮一般对应的是一个接口
然后交给 Security框架
springboot的安全框架,java,spring boot,安全,oracle,Security

第二步验证成功后,给前端返回token 那里从Security框架里拿出 按钮权限
然后存到redis里
springboot的安全框架,java,spring boot,安全,oracle,Security

第三步用户在请求接口的时候 这个时候把从redis里把当前用户的按钮权限拿出来
然后验证
springboot的安全框架,java,spring boot,安全,oracle,Security

第四步 在Security配置文件中添加上redis配置
springboot的安全框架,java,spring boot,安全,oracle,Security

第五步 在控制器里 controller 里添加权限注解
@PreAuthorize(“hasAuthority(‘bnt.sysRole.list’)”)
//bnt.sysRole.list 这个值 就是存在数据库里的按钮的字段 前端和后端可以共同使用
springboot的安全框架,java,spring boot,安全,oracle,Security

最后在定义以下异常处理类

  /**
     * spring security异常
     * @param e
     * @return
     */
    @ExceptionHandler(AccessDeniedException.class)
    @ResponseBody
    public Result error(AccessDeniedException e) throws AccessDeniedException {
        return Result.fail().code(205).message("没有操作权限");
    }

这样 验证和 授权 就全部完成了文章来源地址https://www.toymoban.com/news/detail-735353.html

到了这里,关于spring boot中常用的安全框架 Security框架 利用Security框架实现用户登录验证token和用户授权(接口权限控制)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Security(安全框架)

    (1)Spring Security 是一个高度自定义的 安全框架 。利用Spring IoC/DI和AOP功能,为系统提供了声明式安全访问控制功能,减少了为系统安全而编写大量重复代码的工作。 (2)认证(Authentication): 应用程序确认用户身份的过程,常见认证:登录。 (3)身份(principal)/主体(

    2023年04月13日
    浏览(74)
  • Spring Boot安全管理—Spring Security基本配置

    1.1 创建项目,添加依赖 创建一个Spring Boot Web 项目,然后添加spring-boot-starter-security依赖。 1.2 添加hello接口 在项目中添加一个简单的/hello接口,内容如下: 1.3 启动项目测试 访问/hello接口会自动跳转到登录页面,这个页面有Spring Security提供的。 默认的用户名是user,默认的登

    2024年02月08日
    浏览(51)
  • 【Spring Security】安全框架学习(十二)

    6.0 其他权限校验方法 我们前面都是使用@PreAuthorize注解,然后在在其中使用的是hasAuthority方法进行校验。Spring Security还为我们提供了其它方法. 例如:hasAnyAuthority,hasRole,hasAnyRole,等。 这里我们先不急着去介绍这些方法,我们先去理解hasAuthority的原理,然后再去学习其他方法就

    2024年02月07日
    浏览(50)
  • 【Spring Security】安全框架学习(八)

    3.0 权限系统的作用 例如一个学校图书馆的管理系统,如果是普通学生登录就能看到借书还书相关的功能,不可能让他看到并且去使用添加书籍信息,删除书籍信息等功能。但是如果是一个图书馆管理员的账号登录了,应该就能看到并使用添加书籍信息,删除书籍信息等功能

    2024年02月09日
    浏览(60)
  • 安全框架Spring Security是什么?如何理解Spring Security的权限管理?

    大家好,我是卷心菜。本篇主要讲解Spring Security的基本介绍和架构分析,如果您看完文章有所收获,可以三连支持博主哦~,嘻嘻。 🎁 作者简介: 在校大学生一枚,Java领域新星创作者,Java、Python正在学习中,期待和大家一起学习一起进步~ 💗 个人主页:我是一棵卷心菜的

    2024年02月02日
    浏览(55)
  • 【8】Spring Boot 3 集成组件:安全组件 spring security【官网概念篇】

    个人主页: 【⭐️个人主页】 需要您的【💖 点赞+关注】支持 💯 📖 本文核心知识点: spring security B 官网Doc : Spring Security Spring Security 是一个框架,它提供 身份验证 、 授权 和针对常见 攻击的保护 。它具有保护命令式和响应式应用程序的一流支持,是保护基于spring的应用

    2024年02月03日
    浏览(55)
  • Shiro和Spring Security安全框架对比

    Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单。与Spring Security对比,Shiro可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。下面对这两个安全框架进行了对比,可以

    2024年02月10日
    浏览(41)
  • 【Spring Security】让你的项目更加安全的框架

    🎉🎉欢迎来到我的CSDN主页!🎉🎉 🏅我是Java方文山,一个在CSDN分享笔记的博主。📚📚 🌟推荐给大家我的专栏《Spring Security》。🎯🎯 👉点击这里,就可以查看我的主页啦!👇👇 Java方文山的个人主页 🎁如果感觉还不错的话请给我点赞吧!🎁🎁 💖期待你的加入,一

    2024年02月04日
    浏览(58)
  • Spring Security实现用户身份验证及权限管理

    Spring Security是Spring生态的一个成员,提供了一套Web应用安全性的完整解决方案。 Spring Security 旨在以一种自包含的方式进行操作,因此你不需要在 Java 运行时环境中放置任何特殊的配置文件。这种设计使部署极为方便,因为可以将目标 工件 (无论是 JAR还是WAR)从一个系统复

    2024年02月05日
    浏览(40)
  • Spring Boot 3 + JWT + Security 联手打造安全帝国:一篇文章让你掌握未来!

    Spring Security 已经成为 java 后台权限校验的第一选择.今天就通过读代码的方式带大家深入了解一下Security,本文主要是基于开源项目spring-boot-3-jwt-security来讲解Spring Security + JWT(Json Web Token).实现用户鉴权,以及权限校验. 所有代码基于 jdk17+ 构建.现在让我们开始吧! Springboot 3.0 Spri

    2024年02月07日
    浏览(75)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包