当前位置: 代码网 > it编程>编程语言>Java > SpringBoot中SpringSecurity安全框架的基本配置与使用方式

SpringBoot中SpringSecurity安全框架的基本配置与使用方式

2026年03月13日 Java 我要评论
spring security是一个基于spring框架的安全性解决方案,提供了全面的安全功能和集成能力,用于保护java应用程序的身份验证、授权和其他安全需求。spring security的主要功

spring security是一个基于spring框架的安全性解决方案,提供了全面的安全功能和集成能力,用于保护java应用程序的身份验证、授权和其他安全需求。

spring security的主要功能包括

  • 身份验证(authentication):spring security提供了多种身份验证机制。
  • 授权(authorization):spring security支持基于角色和权限的授权机制,可以精确控制用户对系统资源的访问权限。它提供了注解和标签,使开发人员可以在代码中灵活定义和配置授权规则。
  • 认证流程的安全控制:spring security提供了很多机制来确保认证流程的安全性。
  • web安全:spring security可以通过过滤器链的方式保护web应用程序的安全性。
  • 方法级安全:spring security允许在方法级别上进行安全控制,通过注解或xml配置来限制对特定方法的访问权限。
  • 安全事件和审计:spring security提供了安全事件机制,可以记录和处理安全相关的事件,例如登录成功、权限拒绝等。它还支持审计功能,可用于记录和追踪用户的操作行为。

spring security是一个功能强大、灵活可扩展的安全框架,可以帮助开发人员在java应用程序中实现全面的身份验证和授权功能,提高应用程序的安全性和可信度。

在springboot中使用首先需要导入依赖

<!--        springsecurity-->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-security</artifactid>
        </dependency>

配置securityconfig和jwt工具类

在securityconfig中可以对访问权限进行设置,将登录以及注册接口设置为开放(不然系统就无法访问),也可以将不需要进行权限认证的接口也再此设置为开发;同时也可以通过注解的方式对需要进行权限认证的接口进行设置。

@configuration
@enableglobalmethodsecurity(prepostenabled = true)
public class securityconfig extends websecurityconfigureradapter {

    @bean
    public passwordencoder passwordencoder() {
        return  new bcryptpasswordencoder();
    }

    @autowired
    private jwtauthenticationtokenfilter filter;

    @autowired
    private authenticationentrypoint authenticationentrypoint;

    @resource
    private accessdeniedhandler accessdeniedhandler;
    @override
    protected void configure(httpsecurity http) throws exception {
        http
                //关闭csrf
                .csrf().disable()
                //不通过session获取securitycontext
                .sessionmanagement().sessioncreationpolicy(sessioncreationpolicy.stateless)
                .and()
                .authorizerequests()

                // 对于登录接口 允许匿名访问 未登录状态也可以访问
                .antmatchers("/login/login").anonymous()
                .antmatchers("/login/register").anonymous()
                .antmatchers("/login/sendcode").anonymous()
                .antmatchers("/pay/notify").anonymous()
                // 需要用户带有管理员权限
//                .antmatchers("/find").hasrole("管理员")
//                // 需要用户具备这个接口的权限
//                .antmatchers("/find").hasauthority("menu:user")

                // 除上面外的所有请求全部需要鉴权认证
                .anyrequest().authenticated();
        //添加过滤器
        http.addfilterbefore(filter, usernamepasswordauthenticationfilter.class);

        //配置异常处理器
        http.exceptionhandling()
                //配置认证失败处理器
                .authenticationentrypoint(authenticationentrypoint)
                .accessdeniedhandler(accessdeniedhandler);

        //允许跨域
        http.cors();
    }

    @bean
    @override
    public authenticationmanager authenticationmanagerbean() throws exception {
        return super.authenticationmanagerbean();
    }
}
public class jwtutil {

    //有效期为
    public static final long jwt_ttl = 60 * 60 *1000l;// 60 * 60 *1000  一个小时
    //设置秘钥明文
    public static final string jwt_key = "sangeng";

    public static string getuuid(){
        string token = uuid.randomuuid().tostring().replaceall("-", "");
        return token;
    }

    /**
     * 生成jtw
     * @param subject token中要存放的数据(json格式)
     * @return
     */
    public static string createjwt(string subject) {
        jwtbuilder builder = getjwtbuilder(subject, null, getuuid());// 设置过期时间
        return builder.compact();
    }

    /**
     * 生成jtw
     * @param subject token中要存放的数据(json格式)
     * @param ttlmillis token超时时间
     * @return
     */
    public static string createjwt(string subject, long ttlmillis) {
        jwtbuilder builder = getjwtbuilder(subject, ttlmillis, getuuid());// 设置过期时间
        return builder.compact();
    }

    private static jwtbuilder getjwtbuilder(string subject, long ttlmillis, string uuid) {
        signaturealgorithm signaturealgorithm = signaturealgorithm.hs256;
        secretkey secretkey = generalkey();
        long nowmillis = system.currenttimemillis();
        date now = new date(nowmillis);
        if(ttlmillis==null){
            ttlmillis=jwtutil.jwt_ttl;
        }
        long expmillis = nowmillis + ttlmillis;
        date expdate = new date(expmillis);
        return jwts.builder()
                .setid(uuid)              //唯一的id
                .setsubject(subject)   // 主题  可以是json数据
                .setissuer("sg")     // 签发者
                .setissuedat(now)      // 签发时间
                .signwith(signaturealgorithm, secretkey) //使用hs256对称加密算法签名, 第二个参数为秘钥
                .setexpiration(expdate);
    }

    /**
     * 创建token
     * @param id
     * @param subject
     * @param ttlmillis
     * @return
     */
    public static string createjwt(string id, string subject, long ttlmillis) {
        jwtbuilder builder = getjwtbuilder(subject, ttlmillis, id);// 设置过期时间
        return builder.compact();
    }

    public static void main(string[] args) throws exception {
        string jwt = createjwt("1234");
        claims claims = parsejwt(jwt);
        string subject = claims.getsubject();

        system.out.println(subject);
    }

    /**
     * 生成加密后的秘钥 secretkey
     * @return
     */
    public static secretkey generalkey() {
        byte[] encodedkey = base64.getdecoder().decode(jwtutil.jwt_key);
        secretkey key = new secretkeyspec(encodedkey, 0, encodedkey.length, "aes");
        return key;
    }

    /**
     * 解析
     *
     * @param jwt
     * @return
     * @throws exception
     */
    public static claims parsejwt(string jwt) throws exception {
        secretkey secretkey = generalkey();
        return jwts.parser()
                .setsigningkey(secretkey)
                .parseclaimsjws(jwt)
                .getbody();
    }


}

定义用户类

实现userdetails接口,对用户权限进行封装。

@data
@noargsconstructor
public class loginuser implements userdetails {

    private user user;

    private list<string> permissions;

    public loginuser(user user, list<string> permissions) {
        this.user = user;
        this.permissions = permissions;
    }


    //返回权限信息

    @jsonfield(serialize = false)  //不需要存到redis中,进行序列化忽略
    private list<simplegrantedauthority> authorities;
    @override
    public collection<? extends grantedauthority> getauthorities() {

        if (authorities != null){
            return authorities;
        }
       authorities = permissions.stream()
                .map(simplegrantedauthority::new)
                .collect(collectors.tolist());

        return authorities;
    }

    @override
    public string getpassword() {
        return user.getuserpwd();
    }

    @override
    public string getusername() {
        return user.getusername();
    }

    //判断是否没过期
    @override
    public boolean isaccountnonexpired() {
        return true;
    }

    @override
    public boolean isaccountnonlocked() {
        return true;
    }

    @override
    public boolean iscredentialsnonexpired() {
        return true;
    }

    @override
    public boolean isenabled() {
        return true;
    }
}

实现userdetailsservice接口

重写loaduserbyusername方法查询用户权限信息

@override
public userdetails loaduserbyusername(string username) throws usernamenotfoundexception {

    //查询用户信息
    lambdaquerywrapper<user> wrapper = new lambdaquerywrapper<>();
    wrapper.eq(user::getusername, username);
    user user = usermapper.selectone(wrapper);
    //如果没有查询到用户就抛出异常
    if (objects.isnull(user)){
        throw new runtimeexception("用户名或密码错误");
    }
    list<string> list = menumapper.selectpermsbyuserid(user.getid());

    return new loginuser(user,list);
}

配置过滤器

@component
public class jwtauthenticationtokenfilter extends onceperrequestfilter {

    @autowired
    private rediscache rediscache;

    @override
    protected void dofilterinternal(httpservletrequest request, httpservletresponse response, filterchain filterchain) throws servletexception, ioexception {
        string token = request.getheader("token");

        if (!stringutils.hastext(token)) {
            //token为空,放行
            filterchain.dofilter(request, response);
            return;
        }
        //解析token
        string userid;
        try {
            claims claims = jwtutil.parsejwt(token);
            userid = claims.getsubject();
        } catch (exception e) {
            throw new runtimeexception("token非法");
        }
        //从redis中获取用户信息
        string rediskey = "login:" + userid;

        loginuser loginuser = rediscache.getcacheobject(rediskey);
        if (objects.isnull(loginuser)){
            throw new runtimeexception("用户未登录");
        }
        //存入securitycontextholder
        // 获取权限信息封装到authentication中
        usernamepasswordauthenticationtoken usernamepasswordauthenticationtoken =
                new usernamepasswordauthenticationtoken(loginuser, null, loginuser.getauthorities());

        securitycontextholder.getcontext().setauthentication(usernamepasswordauthenticationtoken);
        //放行
        filterchain.dofilter(request, response);
    }
}

配置认证失败和权限不足的返回类

这两个方法,在securityconfig中进行添加。当对于清空出现时,调用方法,返回给前端状态码,以及自定义信息。

@component
public class authenticationentrypointimpl implements authenticationentrypoint {
    @override
    public void commence(httpservletrequest request, httpservletresponse response, authenticationexception authexception) throws ioexception, servletexception {
        result result = new result(httpstatus.unauthorized.value(), "认证失败");
        string json = json.tojsonstring(result);
        //处理异常
        webutils.renderstring(response, json);

    }
}
@component
public class accessdeniedhandlerimpl implements accessdeniedhandler {
    @override
    public void handle(httpservletrequest request, httpservletresponse response, accessdeniedexception accessdeniedexception) throws ioexception, servletexception {
        result result = new result(httpstatus.forbidden.value(), "权限不足");
        string json = json.tojsonstring(result);
        webutils.renderstring(response, json);
    }
}

配置完成后,在前端页面访问后端接口,过滤器会进行拦截,判断前端请求中请求头是否携带token,携带并成功验证用户后,会将用户的权限信息封装到authentication中,后续判断该用户是否拥有访问该接口的权限。

例如设置下面接口的访问中需要用户拥有getuseraddress权限。

@requestmapping("/getuseraddress")
@preauthorize("hasauthority('getuseraddress')")
public result getuseraddress(long userid) {

    return addressservice.getuseraddress(userid);
}

用户、角色、权限表信息如下

可以看到用户1不存在访问getuseraddress的权限。

当使用用户1登录后,在前端对该接口进行访问时,后端会返回权限不足的状态码以及相应的信息信息。该接口的方法也不会执行。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com