SpringSecurity做成微服务

前言

第一次使用spring security,前后端分离方式。前端vue+element,后端springboot。
搜索了一些文档,基础的登录功能已经实现。

  1. spring security配置之WebSercurityConfig
package com.website.server.system.security;

import com.website.server.system.security.hander.LoginFailureHandler;
import com.website.server.system.security.hander.LoginSuccessHandler;
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * @author :qilong sun
 * @date :Created in 2019/11/27 16:56
 * @description:security配置
 * @modified By:
 * @version: V1.0$
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        // 开启授权认证
        httpSecurity.authorizeRequests().anyRequest().authenticated();
        // 配置登录
        httpSecurity.formLogin().usernameParameter("loginAccount").passwordParameter("loginPwd").loginProcessingUrl("/toLogin");
        // 登录成功处理
        httpSecurity.formLogin().successHandler(new LoginSuccessHandler());
        // 登录失败处理
        httpSecurity.formLogin().failureHandler(new LoginFailureHandler());
        // csrf配置
        httpSecurity.csrf();
        // 开启跨域共享,跨域伪造请求限制=无效
        httpSecurity.cors().and().csrf().disable();
    }
}

  1. spring security之自定义userDetailsService
package com.website.server.system.security;

import com.core.server.dto.security.SecurityPermission;
import com.core.server.dto.security.SecurityRoleDto;
import com.core.server.dto.security.SecurityUserDto;
import com.core.server.service.api.user.BaseUserFeignServiceApi;
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;

/**
 * @author :qilong sun
 * @date :Created in 2019/11/27 14:00
 * @description:用户权限;参考教程:https://blog.csdn.net/cloume/article/details/83790111
 * @modified By:
 * @version: V1.0$
 */
@Service
public class MyUserDetailService implements UserDetailsService {
    @Autowired
    BaseUserFeignServiceApi baseUserFeignServiceApi;

    @Override
    public UserDetails loadUserByUsername(String username) {
        // 根据用户名查询用户
        SecurityUserDto user = baseUserFeignServiceApi.querySecurityUserDtoByOne(username);
        if (null == user) {
            throw new UsernameNotFoundException(String.format("No user found with username: %s", username));
        }
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        List<String> roles = user.getRoles();
        if (null != roles) {
            for (String roleName : roles) {
                // 根据角色名称查询角色
                SecurityRoleDto securityRoleDto = baseUserFeignServiceApi.querySecurityRoleDtoByCodeName(roleName);
                if (null == securityRoleDto || null == securityRoleDto.getPermissions() || securityRoleDto.getPermissions().size() == 0) {
                    continue;
                }
                for (SecurityPermission.ResourcesDto permission : securityRoleDto.getPermissions()) {
                    for (String privilege : permission.getPrivileges().keySet()) {
                        authorities.add(new SimpleGrantedAuthority(String.format("%s-%s", permission.getResourceId(), privilege)));
                    }
                }
            }
        }
        //  isEnabled=是否启用; isAccountNonExpired=账户是否过期; isCredentialsNonExpired=凭证是否过期; isAccountNonLocked=账户是否锁定
        return new org.springframework.security.core.userdetails.User(user.getUsername(), "{noop}" + user.getPassword(), user.getIsEnabled(), user.getIsAccountNonExpired(), user.getIsCredentialsNonExpired(), user.getIsAccountNonLocked(), authorities);
    }
}

  1. 登录
    在这里插入图片描述
    在这里插入图片描述
  2. 获取用户信息

在这里插入图片描述

{
    "authorities": [
        {
            "authority": "privilege-delete"
        },
        {
            "authority": "privilege-read"
        },
        {
            "authority": "privilege-update"
        },
        {
            "authority": "privilege-write"
        },
        {
            "authority": "user-delete"
        },
        {
            "authority": "user-read"
        },
        {
            "authority": "user-update"
        },
        {
            "authority": "user-write"
        }
    ],
    "details": {
        "remoteAddress": "0:0:0:0:0:0:0:1",
        "sessionId": null
    },
    "authenticated": true,
    "principal": {
        "password": null,
        "username": "1441101265",
        "authorities": [
            {
                "authority": "privilege-delete"
            },
            {
                "authority": "privilege-read"
            },
            {
                "authority": "privilege-update"
            },
            {
                "authority": "privilege-write"
            },
            {
                "authority": "user-delete"
            },
            {
                "authority": "user-read"
            },
            {
                "authority": "user-update"
            },
            {
                "authority": "user-write"
            }
        ],
        "accountNonExpired": true,
        "accountNonLocked": true,
        "credentialsNonExpired": true,
        "enabled": true
    },
    "credentials": null,
    "name": "1441101265"
}

目标

现在想把spring security抽取出来,做成通用的。
方法:

  1. 做成一个jar
    做成jar的话,和直接写在一个项目里效果一样。
  2. 做成服务
    做成服务的话,还可以自定义一些其他接口,使用的相对更灵活一点,难度相对也更大点。

思路

上面已经介绍了是怎么实现的。如果前端登录,会走userDetailsService,查询用户信息,用户角色,用户权限等。然后把结果,通过处理器,封装后,返回给前台,这就是登录流程。
下面开始做。
在这里插入图片描述
首先,项目已经建好,依赖已经加好,代码,也已经复制到项目里面。

  • aplication.yml,里面配置是端口,以及注册中心信息
  • ***Application是启动类
  • system.security下面是上面图片里面的代码。hander是成功处理,失败处理。另外就是filter(具体干嘛用的,好像加它是因为处理器还是啥的,记不清了,后面需要的时候再查下)。
    变动代码:webSecurityConfig,因为现在是通过服务方式调用,按照上面的配置,我是需要先登录的。所以现在先把拦截都放开,至于是否需要配置,后面再说。否则测试的时候,调接口都调不通。这是现在的代码:
/**
 * @author :qilong sun
 * @date :Created in 2019/11/27 16:56
 * @description:security配置
 * @modified By:
 * @version: V1.0$
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .authorizeRequests().antMatchers(
                "/js/**",
                "/css/**",
                "/img/**",
                "/login/**").permitAll()
                .anyRequest().permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService());
    }
}
  • SecurityController,控制器。专门提供接口的。
    现在,我把userDetailsService注入到controller里面。然后调用登录的方法。
package com.core.server.controller;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;

/**
 * @author :qilong sun
 * @date :Created in 2019/12/20 19:42
 * @description:security控制器
 * @modified By:
 * @version: $1.0
 */
@RestController
@RequestMapping("/security")
public class SecurityController {
    @Resource
    UserDetailsService myUserDetailServiceImpl;

    /**
     * 测试获取用户信息
     * @return
     */
    @GetMapping("/testGetUser2")
    public Principal getUser2(HttpServletRequest request) {
        return request.getUserPrincipal();
    }

    /**
     * 根据账号登录
     * @param username
     * @return
     */
    @GetMapping("/loginByUsername")
    public UserDetails loginByUsername(@RequestParam("username") String username){
        UserDetails userDetails = myUserDetailServiceImpl.loadUserByUsername(username);
        return userDetails;
    }
}


整个项目,代码如上。
通过postman,现在是可以跑到这个loginByUsername方法。返回信息如下:
在这里插入图片描述
问题:如果security没有做成服务,那么调用登录,是会在cookie里面生成一个jssessionid的,返回给前台,放到请求信息里面,然后前台每次请求的时候,都带上它。这个jssessionid代表了当前用户,包含了用户信息,角色信息,权限信息等。当然,上图返回的信息比较少。是因为登录后,如果获取用户信息,一般是通过这个方法获取的,上面postman返回的就是:

/**
     * 测试获取用户信息
     * @return
     */
    @GetMapping("/testGetUser2")
    public Principal getUser2(HttpServletRequest request) {
        return request.getUserPrincipal();
    }

可是现在通过myUserDetailServiceImpl.loadUserByUsername(username); 是没有token的。返回的是有资源信息,但是这是在security服务。把它返回到消费者服务,怎么让controller里方法上面的 @PreAuthorize(“hasRole(‘user’)”) 注解生效,没有思路。

参考文档

待补充

解决方法

待补充

结果

待补充

遇到的问题

发布了48 篇原创文章 · 获赞 14 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/s1441101265/article/details/103640997
今日推荐