Spring Security 使用核心

1.导入依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-jwt</artifactId>
        <version>1.0.10.RELEASE</version>
    </dependency>

2.认证服务配置   

package com.itheima.auth.config;

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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

/**
 * 开启认证服务器配置类
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private TokenStore tokenStore;


    @Autowired
    private AuthenticationManager authenticationManager;



    /**
     * 注册bcrypt加密对象
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 定义用户详情服务:框架会调用该对象 UserDetailsService(封装用户名密码,权限)  查询用户信息用于判断用户认证信息合法。
     * TODO:将来将用户存入MySQL数据库中
     *
     * @return
     */
    @Bean
    public UserDetailsService userDetailsService(){
        //在内存中提供自定义的用户名 密码
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        //在内存中自定义用户名称:jack 密码:jack 权限:p1
        manager.createUser(User.withUsername("third").password(passwordEncoder().encode("third")).authorities("user:query").build());

        manager.createUser(User.withUsername("jack").password(passwordEncoder().encode("jack")).authorities("p1").build());
        manager.createUser(User.withUsername("rose").password(passwordEncoder().encode("rose")).authorities("p2").build());
        return manager;
    }

    /**
     * 配置客户端详情:移动端、web端,第三方应用 定义客户端ID,秘钥
     * third_client:提供授权码模式测试客户端  适用其他的第三方系统
     * app_client,pc_client 密码模式     适用受信任客户端
     * @param clients
     * - clientId:(必须的)用来标识客户的Id(理解为第三方应用账户)
     * - secret:(需要值得信任的客户端)客户端安全码,如果有的话。
     * - scope:用来限制客户端的访问范围,可选值(read,write,all)如果为空(默认)的话,那么客户端拥有全部的访问范围。
     * - authorizedGrantTypes:此客户端可以使用的授权类型,默认为空。 可选值( **`authorization_code`** , **`password`,`implicit,client_credentials,refresh_token`** )
     * - authorities:此客户端可以使用的权限(基于Spring Security authorities)。
     * - autoApproveScopes:  设置是否自动授权
     * - redirectUris 授权码模式中重定向地址
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //客户端信息暂存储在内存中,也可改为security提供的表存储
        //授权码模式下需要的客户端信息如下
        clients.inMemory()
                .withClient("third_client")  //客户端标识
                    .secret(passwordEncoder().encode("third_secret"))  //客户端的秘钥
                    .authorizedGrantTypes("authorization_code")
                    .autoApprove(true)
                    .scopes("all")
                    //.autoApprove("all")
                    .redirectUris("http://www.baidu.com")  //设置回调地址
                //密码模式需要的客户端信息


                .and()
                    .withClient("app_client")  //客户端标识
                    .secret(passwordEncoder().encode("app_secret"))  //客户端的秘钥
                    .authorizedGrantTypes("password", "refresh_token")
                    .scopes("all") //设置回调地址

                .and()
                    .withClient("pc_client")
                    .secret(passwordEncoder().encode("pc_secret"))
                    .authorizedGrantTypes("password", "refresh_token")
                    .scopes("all");
    }





    /**
     * 令牌服务支持
     *
     * @return
     */
    @Bean
    public AuthorizationServerTokenServices tokenService() {
        DefaultTokenServices service = new DefaultTokenServices();
        //是否支持刷新令牌
        service.setSupportRefreshToken(true);
        //令牌存储
        service.setTokenStore(tokenStore);
        // 令牌有效期单位秒,默认默认12小时  设置为1周
        service.setAccessTokenValiditySeconds(604800);
        // 刷新令牌默认单位秒  默认30天 设置为3周
        service.setRefreshTokenValiditySeconds(1814400);
        return service;
    }




    /**
     * 注入令牌策略
     * @param endpoints
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager)
                .tokenServices(tokenService());
    }


    /**
     * **令牌端点的安全约束,对密码模式表单提交允许**
     * @param security
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security.allowFormAuthenticationForClients();  //支持密码模式下表单登录
    }
}

3.token加密方式配置

package com.happyu.auth.config;

import jdk.nashorn.internal.parser.Token;
import org.junit.Before;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import java.security.KeyPair;

@Configuration
public class TokenConfig {

    //对称加密采用秘钥
    private static final String secret = "itcast_auth";

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        KeyPair keyPair = new KeyStoreKeyFactory(new ClassPathResource("oauth2.jks"), "itcast".toCharArray()).getKeyPair("oauth2");
        jwtAccessTokenConverter.setKeyPair(keyPair);
//        jwtAccessTokenConverter.setSigningKey(secret);
        return jwtAccessTokenConverter;
    }

    /**
     * 默认:InMemoryTokenStore,内存中生成一个普通的令牌uuid。
     * @return
     */
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }
}

4.密码模式下需要:用户认证时需要的认证管理和用户信息来源

package com.happyu.auth.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.WebSecurityConfigurerAdapter;

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
      /**
     * 密码模式下需要:用户认证时需要的认证管理和用户信息来源
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
   

}

5.配置认证器

package com.happyu.auth.integration.authenticator;

import com.happyu.sys.dto.CompanyUserDTO;

import javax.servlet.http.HttpServletRequest;

/**
 * @author heima
 * 登录认证器接口
 */
public interface LoginHandler {

    /**
     * 远程调用系统微服务得到用户信息
     *
     * @param request Http请求对象
     * @return 用户表实体
     */
    CompanyUserDTO queryUser(HttpServletRequest request);



    /**
     * 认证器各个实现类
     * 判断当前客户端提交认证类型是否支持当前认证器
     *
     * @param request 请求对象
     * @return true:采用当前认证器  false:不支持
     */
    boolean support(HttpServletRequest request);
}

6.自定义UserDetailService

package com.happyu.auth.integration.service;


import cn.hutool.core.collection.CollectionUtil;
import com.happyu.auth.integration.authenticator.LoginHandler;
import com.happyu.common.threadlocals.UserHolder;
import com.happyu.common.util.BeanHelper;
import com.happyu.common.util.JsonUtils;
import com.happyu.common.vo.UserInfo;
import com.happyu.sys.dto.CompanyUserDTO;
import com.happyu.sys.entity.Function;
import com.happyu.sys.entity.Role;
import lombok.extern.slf4j.Slf4j;
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.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;

/**
 * 自定义用户详情服务对象,框架查询该对象方法得到用户信息UserDetails
 * @author: itheima
 * @create: 2021-09-19 11:02
 */
@Slf4j
@Component
public class MyUserDetailsService implements UserDetailsService {

    /**
     * 当前认证器集合
     */
    @Autowired
    private List<LoginHandler> loginHandlers;


    /**
     * 框架认证过程调用该方法得到用户信息
     * @param username 用户名
     * @return 用户详情对象-包含用户名称,用户正确密码,用户权限
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.info("调用到自定义详情服务MyUserDetailsService--查询用户信息");
        //1.获取用户提交认证方式
        //1.1 如果在普通类中获取请求参数 采用RequestContextHolder请求上下文对象
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //1.2 转为ServletRequestAttributes
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        //1.3 获取请求对象 进一步 获取请求参数
        HttpServletRequest request = servletRequestAttributes.getRequest();
        log.info("认证参数:"+request.getParameter("username") +", "+request.getParameter("auth_type"));

        //2.根据客户端认证方式,选择一个认证器进行 获取数据库中 员工信息
        //选择认证器
        LoginHandler loginHandler = chooseLoginHandler(request);
        CompanyUserDTO companyUserDTO = loginHandler.queryUser(request);

        //3.封装框架要求的返回结果 UserDetails 用户详情服务
        //3.1 封装用户权限信息
        List<GrantedAuthority> authorities = this.getAuthorities(companyUserDTO);
        if (CollectionUtils.isEmpty(authorities)) {
            //如果用户没有权限 增加 游客 角色
            authorities.add(new SimpleGrantedAuthority("ROLE_USER_TOURIST"));
        }

        //3.2 User对象参数一:生成jwt令牌中 user_name 属性值 问题:将来资源服务器无法得到用户ID标识
        //3.3 解决 将CompanyUserDTO转为 自定义对象:UserInfo
        UserInfo userInfo = BeanHelper.copyProperties(companyUserDTO, UserInfo.class);
        //将用户信息存入userholder
        UserHolder.setUser(userInfo);
        return new User(JsonUtils.toJsonStr(userInfo), companyUserDTO.getPassword(), authorities);
        //x.后续框架还会继续进行校验用户信息,客户端信息是否合法 ,最终才会根据用户信息产生令牌
    }


    /**
     * 根据用户角色跟权限 封装框架要求权限对象
     * 将角色,权限字符串 封装权限对象
     * 封装
     * @param companyUserDTO
     * @return
     */
    private List<GrantedAuthority> getAuthorities(CompanyUserDTO companyUserDTO) {
        //1.封装角色权限
        List<GrantedAuthority> authorities = new ArrayList<>();
        List<Role> roleList = companyUserDTO.getSysRoles();
        if (CollectionUtil.isNotEmpty(roleList)) {
            for (Role role : roleList) {
                GrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(role.getRoleName());
                authorities.add(simpleGrantedAuthority);
            }
        }
        //2.封装权限
        List<Function> functionList = companyUserDTO.getSysFunctions();
        if (CollectionUtil.isNotEmpty(functionList)) {
            for (Function function : functionList) {
                GrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(function.getName());
                authorities.add(simpleGrantedAuthority);
            }
        }
        return authorities;
    }


    /**
     * 根据传入请求参数判断 选择一个认证器实例返回
     * @param request
     * @return
     */
    private LoginHandler chooseLoginHandler(HttpServletRequest request) {
        //1.遍历认证器集合
        for (LoginHandler loginHandler : loginHandlers) {
            //2.每个认证器都有support方法 只要support返回true 直接返回当前实例对象
            boolean support = loginHandler.support(request);
            //2.1 进入某个方法实现
            if (support) {
                return loginHandler;
            }
        }
        throw new OAuth2Exception("不支持当前认证方式");
    }
}

6.在需要使用认证的微服务上添加资源配置和token配置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值