spring boot 2.X整合spring security和spring oauth

​ 网上有很多该系列的教程,但是很多都是spring boot1.x,很少看见关于spring boot 2.0 .本人是打算做个spring cloud的web程序,这个整合我就是放在zuul上,类似于做了个网关的鉴权吧。。

国际通用案例 上jar包依赖

<parent>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-parentartifactId>
    <version>2.0.4.RELEASEversion>
    <relativePath/> 
parent>
<properties>
    <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
    <java.version>1.8java.version>
    <spring.druid.version>1.1.10spring.druid.version>
    <spring.mybatis.version>1.3.2spring.mybatis.version>
    <mapper.spring.version>2.0.4mapper.spring.version>
    <pagehelper.spring.version>1.2.6pagehelper.spring.version>
    <jwt.verson>0.9.0jwt.verson>
properties>

 
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-securityartifactId>
dependency>
<dependency>
    <groupId>org.springframework.security.oauthgroupId>
    <artifactId>spring-security-oauth2artifactId>
dependency>
<dependency>
    <groupId>org.springframework.securitygroupId>
    <artifactId>spring-security-jwtartifactId>
dependency>
<dependency>
    <groupId>org.springframework.security.oauth.bootgroupId>
    <artifactId>spring-security-oauth2-autoconfigureartifactId>
dependency>

<dependency>
    <groupId>io.jsonwebtokengroupId>
    <artifactId>jjwtartifactId>
    <version>${jwt.verson}version>
dependency>

我们首先要写登录逻辑

@Component
@Slf4j
public class AppUserDetailsService implements UserDetailsService {
	@Autowired
	private PasswordEncoder passwordEncoder;
	/**
	 * 表单登录
	 */
	@Override
	public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
		return buildUser(userName);
	}
	//用户必须要有ROLE_USER 才可以登录 服务提供商
	private UserDetails buildUser(String userId) {
		// 根据用户名查找用户信息,这里可以写我们的登录逻辑比如说XXXXservice.findUser(String user);
		//根据查找到的用户信息判断用户是否被冻结
		String password = passwordEncoder.encode("123456");
		log.info("数据库密码是:"+password);
		return new User(userId, password,
				true, true, true, true,
				AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN,ROLE_USER"));
	}
}

配置我们的资源服务器@EnableResourceServer

@Configuration
@EnableResourceServer
public class AppWebSecurityConfigurerAdapter extends ResourceServerConfigurerAdapter {

   private final AppSecurityExpressionHandler appSecurityExpressionHandler;
   private final ZuulProperties zuulProperties;
   private final AuthenticationSuccessHandler appAuthenticationSuccessHandler;
   private final AuthenticationFailureHandler appAuthenticationFailureHandler;
   private final AccessDeniedHandler appAccessDeniedHandler;

   public AppWebSecurityConfigurerAdapter(AppSecurityExpressionHandler appSecurityExpressionHandler, ZuulProperties zuulProperties, AuthenticationSuccessHandler appAuthenticationSuccessHandler, AuthenticationFailureHandler appAuthenticationFailureHandler, AccessDeniedHandler appAccessDeniedHandler) {
      this.appSecurityExpressionHandler = appSecurityExpressionHandler;
      this.zuulProperties = zuulProperties;
      this.appAuthenticationSuccessHandler = appAuthenticationSuccessHandler;
      this.appAuthenticationFailureHandler = appAuthenticationFailureHandler;
      this.appAccessDeniedHandler = appAccessDeniedHandler;
   }



   @Override
   public void configure(HttpSecurity http) throws Exception {
      http
         .formLogin()
            //登录失败过滤器
            .failureHandler(appAuthenticationFailureHandler)
          	//配置登录成功过滤器
            .successHandler(appAuthenticationSuccessHandler)
          	//配置登录地址
            .loginPage(ZuulAppConstant.LOGIN_URL)
          	//配置未登录跳转URL
            .loginProcessingUrl(ZuulAppConstant.LOGIN_JUMP_CONTROLLER)
          //配置get不需要验证的URL
         .and()
            .authorizeRequests()
            .antMatchers(HttpMethod.GET,zuulProperties.getAuth().toGetAdapter())
            .permitAll()
          //配置post不需要验证的URL
         .and()
            .authorizeRequests()
            .antMatchers(HttpMethod.POST,zuulProperties.getAuth().toPostAdapter())
            .permitAll()
          //除上述URL,都需要登录用户
         .and()
            .authorizeRequests()
            .anyRequest()
            .authenticated()
          //配置用户无权限登录过滤器
         .and()
            .exceptionHandling().accessDeniedHandler(appAccessDeniedHandler)
         .and()
            .csrf().disable()
            .authorizeRequests()
            .anyRequest()
           //配置授权验证服务
            .access("@defaultZuulAuthorizationService.hasPermission(request,authentication)");
   }
    //和鉴权服务有关,springboot2.0新加入部分
    @Override
	public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
		// TODO Auto-generated method stub
		resources.expressionHandler(appSecurityExpressionHandler);
	}
}

spring security配置,与spring oauth2有关

@EnableWebSecurity
@Order(6)
public class AppSecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Override
    @Bean
    //spring boot 新加的
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

过滤器配置

配置登陆成功过滤器

/**
 * 认证成功跳转
 * @author w4837
 *
 */
@Component(value = "AppAuthenticationSuccessHandler")
@Slf4j
public class AppAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

	@Autowired
	private ObjectMapper objectMapper;
	@Autowired
	private ClientDetailsService clientDetailsService;

	@Override
	@Order
	public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
					HttpServletResponse httpServletResponse, Authentication authentication)
			throws IOException, ServletException {
		log.info("登陆成功");
	}
}

###配置登录失败过滤器

@Slf4j
@Component("AppAuthenticationFailureHandler")
public class AppAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler{

	@Autowired
	private ObjectMapper objectMapper;

	@Override
	public void onAuthenticationFailure(HttpServletRequest request
			, HttpServletResponse response, AuthenticationException exception)
			throws IOException, ServletException {
		log.info("登录失败");
		super.onAuthenticationFailure(request, response, exception);
	}

}

配置无权限服务过滤器

@Component("AppAccessDeniedHandler")
public class AppAccessDeniedHandler implements AccessDeniedHandler{

	@Autowired
	private ObjectMapper objectMapper;
	
	@Override
	public void handle(HttpServletRequest request, HttpServletResponse response,
			AccessDeniedException accessDeniedException) throws IOException, ServletException {
	   response.setContentType(ZuulAppConstant.CONTENT_TYPE_JSON);
       response.setStatus(HttpStatus.SC_FORBIDDEN);
       response.getWriter().write(objectMapper.writeValueAsString(ResultVo.createErrorResult("当前用户访问权限不够,请联系管理员增加对应权限",403)));
	}

}

鉴权服务过滤器 spring boot 2.x新加

@Configuration("appSecurityExpressionHandler")
public class AppSecurityExpressionHandler extends OAuth2WebSecurityExpressionHandler{

	@Bean
    public OAuth2WebSecurityExpressionHandler oAuth2WebSecurityExpressionHandler(ApplicationContext applicationContext) {
        OAuth2WebSecurityExpressionHandler expressionHandler = new OAuth2WebSecurityExpressionHandler();
        expressionHandler.setApplicationContext(applicationContext);
        return expressionHandler;
    }
}

鉴权服务

/**
 * Copyright © 2018 eSunny Info. Tech Ltd. All rights reserved.
 *
 * @author [email protected]
 * @program: app-management
 * @title:ZuulAuthorizationService
 * @Package com.yulece.app.management.zuul.authorization.service
 * @Description:
 * @Date 创建时间 2018/9/30-18:23
 **/
public interface ZuulAuthorizationService {

    /**
     * 验证用户是否有权登录
     * @param request
     * @param authentication
     * @return
     */
    boolean hasPermission(HttpServletRequest request , Authentication authentication);
}


/**
 * Copyright © 2018 eSunny Info. Tech Ltd. All rights reserved.
 *
 * @author [email protected]
 * @program: app-management
 * @title:DefaultRbacService
 * @Package com.yulece.app.management.zuul.authorization.service
 * @Description:
 * @Date 创建时间 2018/9/30-18:58
 **/
@Component("defaultZuulAuthorizationService")
public class DefaultZuulAuthorizationService implements ZuulAuthorizationService {

    private final static Logger LOGGER = LoggerFactory.getLogger(DefaultZuulAuthorizationService.class);

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        boolean isPermission = false;
        String requestURI = request.getRequestURI();
        if(StringUtils.isEmpty(requestURI)){
            return isPermission;
        }
        //匿名用户不能经过授权
        if(authentication!=null&&!authentication.getPrincipal().equals("anonymousUser")&&
                authentication.isAuthenticated()) {

            String userName = (String)authentication.getPrincipal();
            //读取用户所有的Url,可以通过用户服务拿到当前用户服务拿到该用户的能访问的地址
            Set<String> urls = new HashSet<>();
            urls.add("/app/**");
            for (String  url : urls) {
                if (antPathMatcher.match(url, requestURI)) {
                    isPermission = true;
                    LOGGER.info("用户[{}]鉴权,鉴权地址为:{}.",userName,requestURI);
                    break;
                }
            }
            return isPermission;
        }
        return isPermission;
    }
}

外部化配置

@ConfigurationProperties(prefix = "app.zuul")
public class ZuulProperties{

    private AuthProperties auth = new AuthProperties();

    private OAuth2Properties oauth = new OAuth2Properties();

    public OAuth2Properties getOauth() {
        return oauth;
    }

    public void setOauth(OAuth2Properties oauth) {
        this.oauth = oauth;
    }

    public AuthProperties getAuth() {
        return auth;
    }

    public void setAuth(AuthProperties auth) {
        this.auth = auth;
    }


}

public class AuthProperties {


    private List<String> methodGetUrl = Lists.newArrayList();
    private List<String> methodPostUrl = Lists.newArrayList("hello");

    public List<String> getMethodGetUrl() {
        return methodGetUrl;
    }

    public void setMethodGetUrl(List<String> methodGetUrl) {
        this.methodGetUrl = methodGetUrl;
    }

    public List<String> getMethodPostUrl() {
        return methodPostUrl;
    }

    public void setMethodPostUrl(List<String> methodPostUrl) {
        this.methodPostUrl = methodPostUrl;
    }

    public String[] toGetAdapter(){
        if(methodGetUrl.size() > 0 ){
            String[] toBeStored = methodGetUrl.toArray(new String[methodGetUrl.size()]);
            return toBeStored;
        }
        return new String[]{""};
    }
    public String[] toPostAdapter(){
        if(methodPostUrl.size() > 0 ){
            String[] toBeStored = methodPostUrl.toArray(new String[methodPostUrl.size()]);
            return toBeStored;
        }
        return new String[]{""};
    }

}

application.properties配置

##设置不拦截URI
app.zuul.auth.methodGetUrl = \
  /authentication/require,\
  /app/account/**
app.zuul.auth.methodPostUrl=/authentication/form,\
  /login
## 设置oauth2授权认证
app.zuul.oauth.clients[0].clientId = app
## 需要配置一个passwordEncoder我配置的是123456
app.zuul.oauth.clients[0].clientSecret = $2a$10$m9W7aCgZ8HwyMtP/CQY2mecC1k6Zjry28HBimMx2.5SuCJPv08C9y

配置加密类

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

启动类

@SpringBootApplication
//@EnableDiscoveryClient
//@EnableZuulProxy
@EnableConfigurationProperties(ZuulProperties.class)
public class AppOauthApplicationBootstrap {

    public static void main(String[] args) {
        SpringApplication.run(AppOauthApplicationBootstrap.class,args);
    }

}

配置认证服务器

###认证服务器配置

@Configuration
@EnableAuthorizationServer
public class AppAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

   private final AuthenticationManager authenticationManager;
   private final UserDetailsService userDetailsService;
   private final ZuulProperties zuulProperties;
   private final TokenStore tokenStore ;
   @Autowired(required = false)
   private JwtAccessTokenConverter jwtAccessTokenConverter;
   @Autowired(required = false)
   private TokenEnhancer jwtTokenEnhancer;
   private final PasswordEncoder passwordEncoder;

   @Autowired
   public AppAuthorizationServerConfig(AuthenticationManager authenticationManager, UserDetailsService userDetailsService, ZuulProperties zuulProperties, TokenStore tokenStore, PasswordEncoder passwordEncoder) {
      this.authenticationManager = authenticationManager;
      this.userDetailsService = userDetailsService;
      this.zuulProperties = zuulProperties;
      this.tokenStore = tokenStore;
      this.passwordEncoder = passwordEncoder;
   }


   @Override
   public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
      InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
      OAuth2ClientProperties[] clientProperties = zuulProperties.getOauth().getClients();
      if(ArrayUtils.isNotEmpty(zuulProperties.getOauth().getClients())) {
         for (OAuth2ClientProperties oAuth2ClientProperties : clientProperties) {
             builder.withClient(oAuth2ClientProperties.getClientId())
               .secret(oAuth2ClientProperties.getClientSecret())
               //token有效时间
               .accessTokenValiditySeconds(oAuth2ClientProperties.getAccessTokenValiditySeconds())
               //验证模式
               .authorizedGrantTypes("password","authorization_code","client_credentials","implicit","refresh_token")
               //刷新时间
               .refreshTokenValiditySeconds(3600*24*100)
               //跳转地址
               .redirectUris("ws.28ph.cn")
               //权限
               .scopes("all");
         }
      } 
   }

   @Override
   public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
      endpoints.authenticationManager(authenticationManager)
            .tokenStore(tokenStore)
            .userDetailsService(userDetailsService)
            .reuseRefreshTokens(true);
      if(jwtAccessTokenConverter != null && jwtTokenEnhancer!=null) {
         TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
         List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
         tokenEnhancers.add(jwtTokenEnhancer);
         tokenEnhancers.add(jwtAccessTokenConverter);
         enhancerChain.setTokenEnhancers(tokenEnhancers);
         endpoints
               .tokenEnhancer(enhancerChain)
               .accessTokenConverter(jwtAccessTokenConverter);
      }

   }


   @Override
   public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
      security.allowFormAuthenticationForClients()
            .passwordEncoder(passwordEncoder)
            .tokenKeyAccess("permitAll()")
            .checkTokenAccess("isAuthenticated()");
   }
}

TOKEN配置

@Configuration
public class TokenStoreConfiger {



   @Autowired
   private RedisConnectionFactory redisConnectionFactory;

   @Bean
   @ConditionalOnProperty(prefix = "yichao.secuirty.oauth2",name = "storeType",havingValue = "redis")
   public TokenStore redisTokenStore() {
      return new RedisTokenStore(redisConnectionFactory);
   }
    
   // matchIfMissing = true 代表两种验证token方式 如果没有jwt 那么会自动产生UUID 那种 然后存储
   //在redis,所以如果没有jwt配置一定要配置spring-redis-jpa
   @Configuration
   @ConditionalOnProperty(prefix = "yichao.secuirty.oauth2",name = "storeType",havingValue = "jwt",matchIfMissing = true)
   public static class JwtToKenConfiger{

      @Autowired
      private ZuulProperties zuulProperties;

      @Bean
      public TokenStore jwtTokenStore() {
         return new JwtTokenStore(jwtAccessTokenConverter());
      }


      @Bean
      public JwtAccessTokenConverter jwtAccessTokenConverter() {
         JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
         //加密秘钥
         converter.setSigningKey(zuulProperties.getOauth().getoAuth2SigningKey());
         return converter;
      }

      @Bean
      @ConditionalOnMissingBean(name = "jwtTokenEnhancer")
      public TokenEnhancer jwtTokenEnhancer() {
         AppTokenEnhancer appTokenEnhancer = new AppTokenEnhancer();
         return appTokenEnhancer;
      }
   }
}

###JWT配置

public class AppTokenEnhancer implements TokenEnhancer {

   @Override
   public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
      Map<String, Object> info = new HashMap<>();
      info.put("author", "yichao.org");
      ((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(info);
      return accessToken;
   }

}

文章大概就到这里,代码git地址

https://github.com/qq1026290752/app-management/tree/master/app-management-zuul

你可能感兴趣的:(java开发)