oauth2授权认证----授权码模式

oauth2授权认证----授权码模式

直接上代码

搭建授权服务器

授权服务器的作用就是用来生成token,下面的代码主要是用来生成token,配置客户端信息,配置token生成方式

@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private MyBatisClientDetailsService myBatisClientDetailsService;

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private AuthorizationCodeServices authorizationCodeServices;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;
    
    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    /**
     * 1.用来配置客户端信息,即请求令牌的一方,需要颁发客户端标识或客户端秘钥。
     * 查询数据库获取,配置好之后会自动查询客户端信息
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        System.out.println("第三步:配置客户端管理,根据client_id查询");
        clients.withClientDetails(myBatisClientDetailsService);
    }

    /**
     * 2.客户端要来配置令牌,需要配置申请的访问端点(即要访问的URL)和令牌服务(包括令牌如何生成,如何存储。。。)
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager)//密码模式必须要配置认证管理器
                .authorizationCodeServices(authorizationCodeServices())//授权码必须要配
                .userDetailsService(userDetailsService)
                .tokenServices(tokenService())//令牌管理服务
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }

    /**
     * 3.配置令牌端点的安全约束,即什么情况下可以访问该端点
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        System.out.println("第四步:发放令牌 isAuthenticated()");
        security.tokenKeyAccess("permitAll()") //允许所有人请求令牌
                .checkTokenAccess("permitAll()") //已验证的客户端才能请求check_token端点
                .allowFormAuthenticationForClients();
    }

    //令牌管理服务
    @Bean
    public AuthorizationServerTokenServices tokenService() {
        DefaultTokenServices service = new DefaultTokenServices();
        service.setClientDetailsService(myBatisClientDetailsService);//客户端详情服务
        service.setSupportRefreshToken(true);//支持刷新令牌
        service.setTokenStore(tokenStore);
        service.setReuseRefreshToken(false);//刷新token的同时刷新refresh_token
//        service.setTokenEnhancer(accessTokenConverter);
        //令牌增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
        service.setTokenEnhancer(tokenEnhancerChain);
        service.setAccessTokenValiditySeconds(600); // token有效期
        service.setRefreshTokenValiditySeconds(1200); // refresh_token有效期
        return service;
    }

    //授权码模式专用对象
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new JdbcAuthorizationCodeServices(dataSource);
    }

}

配置token相关信息,包括秘钥,使用jwt生成token,并且token增强(就是把相关用户信息放到token中)。

@Configuration
public class TokenConfig {

    private final String SIGNING_KEY = "123456";
    @Autowired
    private ZWUserAuthenticationConverter zwUserAuthenticationConverter;


    @Bean
    public TokenStore tokenStore() {
        //JWT令牌存储方案
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {

        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter() {
            /**
             * 重写增强token的方法
             * 自定义返回相应的信息
             *
             */
            @Override
            public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
                // 与登录时候放进去的UserDetail实现类一致
                ZWUser userInfo = (ZWUser) authentication.getUserAuthentication().getPrincipal();
                /* 自定义一些token属性 ***/
                final Map<String, Object> additionalInformation = new HashMap<>(18);        
                Long userId = userInfo.getUser_id();
                additionalInformation.put("name", name);
                additionalInformation.put("userId", userId);
                ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
                return super.enhance(accessToken, authentication);
            }
        };
        accessTokenConverter.setSigningKey(SIGNING_KEY);
        ((DefaultAccessTokenConverter) accessTokenConverter.getAccessTokenConverter()).setUserTokenConverter(zwUserAuthenticationConverter);
        return accessTokenConverter;
    }
}

用户实体类,要继承security中的user类,用户信息可以通过security拿到,放到token中

/**
 * 扩展用户信息
 * 认证后转换的用户信息
 *
 * @author zhuowen
 * @since 2019/6/21
 */
public class ZWUser extends User  {
    /**
     * 用户ID
     */
    @Getter
    private Long user_id;


    /**
     * 用户姓名
     */
    @Getter
    private String name;

    /**
     * Construct the <code>User</code> with the details required by
     * {@link DaoAuthenticationProvider}.
     *
     * @param username              the username presented to the
     *                              <code>DaoAuthenticationProvider</code>
     * @param password              the password that should be presented to the
     *                              <code>DaoAuthenticationProvider</code>
     * @param enabled               set to <code>true</code> if the user is enabled
     * @param accountNonExpired     set to <code>true</code> if the account has not expired
     * @param credentialsNonExpired set to <code>true</code> if the credentials have not
     *                              expired
     * @param accountNonLocked      set to <code>true</code> if the account is not locked
     * @param authorities           the authorities that should be granted to the caller if they
     *                              presented the correct username and password and the user is enabled. Not null.
     * @throws IllegalArgumentException if a <code>null</code> value was passed either as
     *                                  a parameter or as an element in the <code>GrantedAuthority</code> collection
     */
    public ZWUser( Long user_id,  String username, String name, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
        this.user_id = user_id;
        this.name = name;
    }
}

配置用户信息转换器,主要是为了解决refresh_token后,生成的新token没有用户信息的问题,重写extractAuthentication方法,把用户信息重新返回,刷新refresh_token后,新的token中也会包含用户信息,如果不重写,新的token用户信息会变成用户名

/**
 * 用户认证转化器
 * /根据 oauth/check_token 的结果转化用户信息
 *
 * @author zhuowen
 * @since 2019/06/21
 */
@Component
public class ZWUserAuthenticationConverter extends DefaultUserAuthenticationConverter {
    private static final String USER_ID = "user_id";
    private static final String DEPT_ID = "dept_id";
    private static final String ROLE_ID = "role_id";
    private static final String NAME = "name";
    private static final String TENANT_ID = "tenant_id";
    private static final String N_A = "N/A";

    /**
     * Extract information about the user to be used in an access token (i.e. for resource servers).
     *
     * @param authentication an authentication representing a user
     * @return a map of key values representing the unique information about the user
     */
    @Override
    public Map<String, ?> convertUserAuthentication(Authentication authentication) {
        Map<String, Object> response = new LinkedHashMap<>();
        response.put(USERNAME, authentication.getName());
        if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
            response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
        }
        return response;
    }

    /**
     * Inverse of {@link #convertUserAuthentication(Authentication)}. Extracts an Authentication from a map.
     *
     * @param map a map of user information
     * @return an Authentication representing the user or null if there is none
     */
    @Override
    public Authentication extractAuthentication(Map<String, ?> map) {
        if (map.containsKey(USERNAME)) {
            Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
            String username = (String) map.get(USERNAME);
            String name = (String) map.get(NAME);
            Integer userId = (Integer) map.get("userId");
            ZWUser zwUser = new ZWUser(userId.longValue(),username,name,"N/A",true,true,true,true,authorities);
//            String userStr =  (String)map.get("userInfo");
//            System.out.println("userObj=========="+userStr);
//            ObjectMapper objectMapper = new ObjectMapper();
//            ZWUser user = objectMapper.convertValue(userObj, ZWUser.class);
//            System.out.println("user=========="+user.toString());
//            System.out.println(jsonObject.toJSONString());
//            SysUser user = JSON.parseObject(jsonObject.toJSONString(), SysUser.class);
            return new UsernamePasswordAuthenticationToken(zwUser, N_A, authorities);
        }
        return null;
    }

    private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map) {
        Object authorities = map.get(AUTHORITIES);
        if (authorities instanceof String) {
            return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
        }
        if (authorities instanceof Collection) {
            return AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils
                    .collectionToCommaDelimitedString((Collection<?>) authorities));
        }
        throw new IllegalArgumentException("Authorities must be either a String or a Collection");
    }

授权类就完成了,主要是设置token生成方式,使用jwt方式存储,配置token增强,从数据库查询客户端信息,重写extractAuthentication方法

接下来我们写资源管理类,说的通俗点就是生成token后,每次访问接口都需要验证token,网关只是进行一些过滤(也有把token验证放在网关的),并不会验证token,我的验证token的方式写在各个微服务中,如果token验证通过了,就可以访问接口

搭建资源服务器

资源服务器比较简单,把生成token的方式移到资源服务器就OK了,生成token的方式我们用jwt,解析当然也用jwt,所以注入TokenStore ,tokenStore(tokenStore)就是用jwt的方式解析token。

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
    @Autowired
    private TokenStore tokenStore;


    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources//该客户端拥有的资源id
//                .tokenServices(tokenService()) //验证客户端的令牌
//                .authenticationEntryPoint(new LLGAuthenticationEntryPoint())
                .tokenStore(tokenStore)
                .stateless(true);
        System.out.println("资源管理:令牌验证2");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {

        http
                .authorizeRequests()
                .antMatchers("/get/**").permitAll()
                .anyRequest().authenticated()
                .and().csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

token配置类,生成token的秘钥要和解析token的秘钥一样

@Configuration
public class TokenConfig {

    private final String SIGNING_KEY = "12342";
    @Autowired
    private ZWUserAuthenticationConverter zwUserAuthenticationConverter;

    @Bean
    public TokenStore tokenStore() {
        //JWT令牌存储方案
        return new JwtTokenStore(accessTokenConverter());
    }
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {

        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter() {
            /**
             * 重写增强token的方法
             * 自定义返回相应的信息
             *
             */
            @Override
            public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
                // 与登录时候放进去的UserDetail实现类一直查看link{SecurityConfiguration}
                ZWUser userInfo = (ZWUser) authentication.getUserAuthentication().getPrincipal();
                /* 自定义一些token属性 ***/
                final Map<String, Object> additionalInformation = new HashMap<>(18);
                Long userId = userInfo.getUser_id();
                additionalInformation.put("userId", userId);
                ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
                return super.enhance(accessToken, authentication);
            }
        };
        accessTokenConverter.setSigningKey(SIGNING_KEY);
        ((DefaultAccessTokenConverter) accessTokenConverter.getAccessTokenConverter()).setUserTokenConverter(zwUserAuthenticationConverter);
        return accessTokenConverter;
    }
}

这样资源服务器也搭建完成,我的是授权和资源是放在两个不同的服务,也可以放在同一个服务下,看你们自己怎么设计了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值