common-security使用说明
chenweida editó esta página hace 6 años
spring-cloud-starter-oauth2  1.2.0 
spring-security  4.2.3
spring-boot-starter-data-redis  4.5.3


相关资料:

https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/

1.依赖 common-security 工程

        <dependency>
            <groupId>com.yihu.base</groupId>
            <artifactId>common-security</artifactId>
            <version>${版本以项目中最新的版本为主}</version>
        </dependency>
        

2.在yml中添加security需要的配置(redis必须配置 其他不配置会采用默认配置)

spring:
  redis:
    host: 172.19.103.88 # Redis server host.
    port: 6379 # Redis server port.
    database: 1

  aop:
    proxy-target-class: true



security:
  oauth2:
    token:
      accessTokenValidityHours: 2 # 2小时
      refreshTokenValidityHours: 2 # 2小时
      tokenType: accessToken
    sms:
      expireIn: 1 ##1分钟过期
      length: 6 #验证码长度

3.必须扩展的方法

3.1扩展自己的平台客户端体系

实现接口 com.yihu.base.security.rbas.ClientServiceProvider

@Component("baseClientDetailsService")
public class ClientService implements ClientServiceProvider {
    /**
     * 根据自己的业务查询表 返回相关的平台用户信息
     * @param clientId
     * @return
     * @throws ClientRegistrationException
     */
    @Override
    public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
//        SaasDO saasDO = saasDao.findByAppId(clientId);
//        if (saasDO == null) {
//            throw new ClientRegistrationException("用户没有注册");
//        }

        SaasDO baseClientDetails = new SaasDO();
        baseClientDetails.setAppId("cwd");
        baseClientDetails.setAppSecret("cwd");

        baseClientDetails.getAuthorizedGrantTypes();
        return baseClientDetails;
    }
}

3.2.1扩展自己的系统用户体系

实现接口 org.springframework.security.core.userdetails.UserDetailsService

@Component
public class UserService implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 我们只需要把用户返回给spring-security 密码框架自己帮我们校验
     *
     * @param userName
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        if ("admin".equals(userName)) {
            System.out.printf("password:" + passwordEncoder.encode("123456"));
            return new User("admin",
                    passwordEncoder.encode("123456"),
                    true,
                    true,
                    true,
                    true
                    , AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_USER") //权限
            );
        } else if ((!StringUtils.isEmpty(userName))&&userName.length() == 11) {
            System.out.printf("password:" + passwordEncoder.encode("123456"));
            return new User("admin",
                    passwordEncoder.encode("123456"),
                    true,
                    true,
                    true,
                    true
                    , AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_USER") //权限
            );
        } else {
            throw new UsernameNotFoundException("用户不存在");
        }
    }

}

3.2.2扩展自己的用户角色体系判断用户是否有权限访问接口

实现接口 com.yihu.base.security.rbas.IRbasService

@Primary
@Service("rbasService")
public class RbasService implements IRbasService {

    private AntPathMatcher antPathMatcher = new AntPathMatcher();


    @Override
    public Boolean hasPerssion(HttpServletRequest request, Authentication authentication) {
        Object principal = authentication.getPrincipal();
        boolean hasPerssion = false;
        if (principal instanceof UserDetails) {
            //获取用户名字
            String username = ((UserDetails) principal).getUsername();


            //获取用户全部权限
            Set<String> uris = new HashSet<>();
            for (String uri : uris) {
                if (antPathMatcher.match(uri, request.getRequestURI())) {
                    hasPerssion = true;
                    break;
                }
            }
        }
        return hasPerssion;
    }

}

3.3如果是使用短信验证码登陆 还需要实现短信发送器

实现接口 com.yihu.base.security.sms.sender.SmsCodeSender

@Service
@Primary
public class MySmsCodeSender implements SmsCodeSender {
    @Override
    public void send(String mobile, String code) {
        System.out.println("发送号码:"+mobile);
        System.out.println("发送验证码:"+code);
    }
}


3.4 某些不需要过滤的路径可以扩展接口com.yihu.base.security.rbas.provider.AuthorizeConfigProvider

例如

@Component
@Order(Integer.MIN_VALUE)
public class PerssionAllAuthorizeConfigProvider implements AuthorizeConfigProvider {


    @Override
    public void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry expressionInterceptUrlRegistry) {

        expressionInterceptUrlRegistry
                .antMatchers(
                        SecurityProperties.formLogin,
                        SecurityProperties.formLoginPage,
                        SecurityProperties.mobileLogin,
                        SecurityProperties.mobileSendSms
                ).permitAll();
    }
}

4.支持的几种登陆模式说明

授权码模式:(一般开放API用)

注:

1. 获取客户端的实现逻辑结合自己的客户端用户体系实现 (com.yihu.base.security.rbas.ClientServiceProvider)

第一步

获取code

GET http://localhost:8060/oauth/authorize?response_type=code&client_id=cwd&redirect_uri=http://example.com&scope=app

参数说明:

response_type=code 固定
client_id=cwd 根据用户表中自己定义的填写
redirect_uri=http://example.com 根据用户表中自己定义的填写
scope=app 固定

第二步

获取token

PSOT http://localhost:8060/oauth/token
header:  Basic {appid}:{appsecuri} 加密  例如 Basic Y3dkOmN3ZA==

{
     "grant_type":"authorization_code", 授权模式固定
     "client_id":"cwd",
     "code":"第一步请求获取的code",
     "redirect_uri":"http://example.com",
     "scope":"app"
}

返回值

{
    "access_token":"bd677e24-2de5-4862-a5e1-8f90a074db42",   默认2小时过期时间 可以配置 ,由于每次请求都需要验证access_token,所以access_token存储在redis
    "token_type":"bearer",
    "refresh_token":"1427b997-ef94-4061-8940-c71da6549acd",  默认2小时过期时间 可以配置 
    "expires_in":43199,
    "scope":"all"
}

刷新token

POST http://localhost:8060/oauth/token

header:  Basic {appid}:{appsecuri} 加密  例如 Basic Y3dkOmN3ZA==
{
     "grant_type":"refresh_token",
     "refresh_token":"bbb36b54-61b2-4d86-aed3-91c5135174c3"
}

返回值

{
    "access_token":"630e2ccc-a5ce-4486-a855-ba755eb3d0d2",
    "token_type":"bearer",
    "refresh_token":"bbb36b54-61b2-4d86-aed3-91c5135174c3",
    "expires_in":43199,
    "scope":"app"
}

自定义账号密码登陆

注:

1. 获取用户的实现逻辑结合自己的用户体系实现 (org.springframework.security.core.userdetails.UserDetailsService)
POST http://localhost:8060/authentication/form

header:  Basic {appid}:{appsecuri} 加密  例如 Basic Y3dkOmN3ZA==

body
{
    "username":"test",
    "password":"123456"
}

返回值

{
    "access_token":"630e2ccc-a5ce-4486-a855-ba755eb3d0d2",
    "token_type":"bearer",
    "refresh_token":"bbb36b54-61b2-4d86-aed3-91c5135174c3",
    "expires_in":43199,
    "scope":"app"
}

自定义手机号短信验证码登陆

第一步

获取短信 注

  1. 短信验证码默认存在redis中(不可配置)
  2. 短信超时时间1分钟(可配置)
  3. 手机号码验证规则可自定义(实现接口com.yihu.base.security.sms.mobile.MobileCheck)
  4. 短信发送器必须自己实现(实现接口com.yihu.base.security.sms.sender.SmsCodeSender)
GET  http://localhost:8060/code/sms

body
{
    "mobile":"13612345678"
}

第二步


POST  http://localhost:8060/authentication/mobile  1. 短信登陆成功验证码会从redis删除

header:  Basic {appid}:{appsecuri} 加密  例如 Basic Y3dkOmN3ZA==

body
{
    "mobile":"test",
    "sms":"246053"
}

返回值

{
    "access_token":"630e2ccc-a5ce-4486-a855-ba755eb3d0d2",
    "token_type":"bearer",
    "refresh_token":"bbb36b54-61b2-4d86-aed3-91c5135174c3",
    "expires_in":43199,
    "scope":"app"
}

获取accesstoken成功之后的访问方式

http://localhost:8060/user

header 
{
"Authorization":"bearer 5fe6b2c3-f69c-4ddc-a36a-367cdf9479a3"      即 bearer accesstoken
}