Let’s talk about the authorization process of distributed Security, just a little bit of water, big guy do not spray!

I. Demand analysis

  1. UUAThe authentication service is responsible for authentication and authorization
  2. All requests go through the gateway to the microservice
  3. The gateway authenticates the client and forwards requests
  4. The gateway willtokenAfter parsing, it is transmitted to micro-service, which is authorized by micro-service

2. Service construction

Code and environment setup ignored, see github.com/hucheng1997…

2.1 Establishment of registration center Eureka

Refer to https://hucheng.blog.csdn.net/article/details/105547229

2.2 Gateway Zuul setup

There are two ways to integrate OAuth2 into the gateway. One is that the authentication server generates JWT tokens, and all requests are verified at the gateway layer to determine permissions. The other is handled by the resource server and the gateway only forwards the request.

We choose the first option, we use the API gateway as the resource server role of OAuth2 to implement access client permission interception, token parsing and forwarding current logged-in user information (jsonToken) to the microservice, so that the downstream microservice needs to care about token format parsing and OAuth2 related mechanisms.

The API gateway is responsible for two main things in the authentication and authorization system:

  1. As aOAuth2.0Is a resource server role to implement access party permission interception
  2. The token parses and forwards information about the currently logged user (clear text)token) for micro services

The micro service also needs to do two things after receiving the clear text token, which contains the identity and permissions of the logged-in user:

  1. User authorization interception (to see if the current user has access to the resource)
  2. Store user information into the current thread context (to facilitate subsequent business logic to retrieve the current user information at any time)

In Zuul, the resource service configuration is defined. The main configuration content is to define some matching rules, which describe what permissions a certain access client needs to access a microservice

@Configuration
public class ResourceServerConfig {

    public static final String RESOURCE_ID = "res1";

    @Configuration
    @EnableResourceServer
    public class UUAServerConfig extends ResourceServerConfigurerAdapter {

        @Autowired
        private TokenStore tokenStore;

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.tokenStore(tokenStore).resourceId(RESOURCE_ID)
                    .stateless(true);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/uua/**").permitAll(); }}@Configuration
    @EnableResourceServer
    public class OrderServerConfig extends ResourceServerConfigurerAdapter {
        @Autowired
        private TokenStore tokenStore;

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.tokenStore(tokenStore).resourceId(RESOURCE_ID)
                    .stateless(true);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests()
                    .antMatchers("/resource/**").access("#oauth2.hasScope('ALL')"); }}}Copy the code

2.2.1 Forwarding plain-text Tokens to microservices

Through Zuul filter, the purpose is to make it easy for downstream microservices to obtain the current login user information (plaintext token).

① Zuul prefilter is implemented to complete the extraction of current login user information and put it into the request for forwarding micro-services

** * Token transfer interception */public class AuthFilter extends ZuulFilter {
    @Override
    public String filterType(a) {
        return "pre";
    }

    @Override
    public int filterOrder(a) {
        return 0;
    }

    @Override
    public boolean shouldFilter(a) {
        return true;
    }

    @Override
    public Object run(a) throws ZuulException {

        RequestContext ctx = RequestContext.getCurrentContext();

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if(! (authenticationinstanceof OAuth2Authentication)) {
            return null;
        }
        OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication;
        Authentication userAuthentication = oAuth2Authentication.getUserAuthentication();

        List<String> authorities = new ArrayList<>();
        userAuthentication.getAuthorities().stream().forEach(s -> authorities.add(((GrantedAuthority) s).getAuthority()));
        OAuth2Request oAuth2Request = oAuth2Authentication.getOAuth2Request();
        Map<String, String> requestParameters = oAuth2Request.getRequestParameters();
        Map<String,Object> jsonToken = new HashMap<>(requestParameters);
        if(userAuthentication! =null){
            jsonToken.put("principal",userAuthentication.getName());
            jsonToken.put("authorities",authorities);
        }

        ctx.addZuulRequestHeader("json-token", EncryptUtil.encodeUTF8StringBase64(JSON.toJSONString(jsonToken)));
        return null; }}Copy the code

② Add filter to spring container

@Configuration
public class ZuulConfig {

    @Bean
    public AuthFilter preFilter(a) {
        return new AuthFilter();
    }

    @Bean
    public FilterRegistrationBean corsFilter(a) {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        config.setMaxAge(18000L);
        source.registerCorsConfiguration("/ * *", config);
        CorsFilter corsFilter = new CorsFilter(source);
        FilterRegistrationBean bean = new FilterRegistrationBean(corsFilter);
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        returnbean; }}Copy the code

2.2.2 Microservices process tokens

The microservice definition filter intercepts the token and forms an Authentication object for Spring Security

@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = request.getHeader("json-token");
        if(token ! =null) {
            String json = EncryptUtil.decodeUTF8StringBase64(token);

            JSONObject jsonObject = JSON.parseObject(json);
            UserDTO userDTO = JSON.parseObject(jsonObject.getString("principal"), UserDTO.class);

            JSONArray authoritiesArray = jsonObject.getJSONArray("authorities");
            String[] authorities = authoritiesArray.toArray(new String[authoritiesArray.size()]);

            UsernamePasswordAuthenticationToken authenticationToken
                    = new UsernamePasswordAuthenticationToken(userDTO, null, AuthorityUtils.createAuthorityList(authorities));
            authenticationToken.setDetails(newWebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } filterChain.doFilter(request, response); }}Copy the code

2.2.3 SpringSecurity User-defined UserDetailsService

@Service
public class SpringDataUserDetailsService implements UserDetailsService {

    @Autowired
    UserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        UserDto userDto = userDao.getUserByUsername(username);
        if(userDto == null) {return null;
        }

        List<String> permissions = userDao.findPermissionsByUserId(userDto.getId());

        String[] permissionArray = new String[permissions.size()];
        permissions.toArray(permissionArray);

        String principal = JSON.toJSONString(userDto);
        UserDetails userDetails = User.withUsername(principal).password(userDto.getPassword()).authorities(permissionArray).build();
        returnuserDetails; }}Copy the code