SpringCloud-技术专区-认证服务操作
一、前言
最近在写微服务认证中心,实现了基本功能,然而到了网关代理请求过去总是未授权,不知道什么原因,先记下来有空再完善。
二、实例
<dependencyManagement> <dependencies> <!-- cloud 组件版本 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.SR2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository>
common pom 引入security
<!--cloud oauth2 服务认证 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-jwt --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> <version>1.0.10.RELEASE</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
我把jwt 相关放到了common里
/** * jwt token 配置类 */ @Configuration public class JWTTokenConfig { @Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter(); accessTokenConverter.setSigningKey("inc-secret"); // 签名密钥 return accessTokenConverter; } @Bean public TokenEnhancer tokenEnhancer() { return new JWTTokenEnhancer(); } }
public class JWTTokenEnhancer implements TokenEnhancer { /** * 向jwt中添加额外信息 * @param oAuth2AccessToken * @param oAuth2Authentication * @return */ @Override public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) { Map<String, Object> info = new HashMap<>(); info.put("org", "chinasoftinc"); ((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(info); return oAuth2AccessToken; } }
认证服务器config
/** * 授权服务配置 * @author wenx * @version 1.0 */ @Configuration @EnableAuthorizationServer @Slf4j public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager;
@Autowired private AuthUserDetailService authUserDetailService;
@Autowired private ClientDetailsService clientDetailsService;
@Autowired private TokenStore jwtTokenStore;
@Autowired private JwtAccessTokenConverter jwtAccessTokenConverter;
@Autowired private TokenEnhancer tokenEnhancer;
@Resource private DataSource dataSource; /** * 配置令牌token的访问端点和令牌服务 * @param endpoints * @throws Exception */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { TokenEnhancerChain enhancerChain = new TokenEnhancerChain(); List<TokenEnhancer> enhancers = new ArrayList<>(); enhancers.add(tokenEnhancer); enhancers.add(jwtAccessTokenConverter); enhancerChain.setTokenEnhancers(enhancers); endpoints.authenticationManager(authenticationManager)//密码模式 .userDetailsService(authUserDetailService) //不添加的话会出错 .tokenStore(jwtTokenStore)//令牌管理服务是必须的 .tokenEnhancer(enhancerChain) .accessTokenConverter(jwtAccessTokenConverter) .allowedTokenEndpointRequestMethods(HttpMethod.POST);//允许post请求访问令牌 } /** * 配置令牌的安全约束 * @param security * @throws Exception */ @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()") //开启/oauth/check_token 验证端口认证权限访问 .allowFormAuthenticationForClients(); //表单认证,申请令牌 } /** * 配置客户端详情,分配客户端标识和客户端密钥 * @param clients * @throws Exception */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.withClientDetails(clientDetailsService); } @Bean public AuthorizationServerTokenServices tokenServices(){ DefaultTokenServices services = new DefaultTokenServices(); services.setClientDetailsService(clientDetailsService); services.setSupportRefreshToken(true); services.setTokenStore(jwtTokenStore); services.setAccessTokenValiditySeconds(7200);//令牌默认有效期2小时 services.setRefreshTokenValiditySeconds(259200);//刷新令牌有效期3天 return services; } @Bean // 声明 ClientDetails实现 public ClientDetailsService clientDetailsService() { return new JdbcClientDetailsService(dataSource); } }
认证服务器webconfig
/** * web安全配置 **/ @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private AuthUserDetailService userServiceDetail; /** * 安全拦截机制 * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { //配置密码模式服务器 http.authorizeRequests() .antMatchers("/oauth/**").permitAll() .antMatchers("/**").authenticated() .and()//HttpSecurity .httpBasic() .and() .csrf().disable()//防csrf攻击 禁用 ; } @Override//通过重载该方法,可配置Spring Security的Filter链(HTTP请求安全处理) public void configure(WebSecurity web) throws Exception { super.configure(web); } //设置密码加密规则 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userServiceDetail).passwordEncoder(passwordEncoder()); } @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } //这一步的配置是必不可少的,否则SpringBoot会自动配置一个AuthenticationManager,覆盖掉内存中的用户 @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
认证服务器userdetailservice
@Slf4j @Service public class AuthUserDetailService implements UserDetailsService { @Autowired private SysUserLoginService sysUserService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // TODO 这个地方可以通过username从数据库获取正确的用户信息,包括密码和权限等。 Sys_user user = sysUserService.queryUserByName(username); if (user == null) { throw new BadCredentialsException("user: " + username + " not found."); } return new User(user.getUsername(), user.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER,ROLE_ADMIN")); } }
资源服务器pom
<dependency> <groupId>com.chinasoftinc</groupId> <artifactId>inc-common</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- eureka 里面已经包含 ribbon 了, 所以不用单独添加, ribbon依赖, 点击依赖就去看就知道了 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency>
资源服务器配置
@Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) public class ResourceServerConfig extends ResourceServerConfigurerAdapter { private static final String SERVER_RESOURCE_ID = "resource_1"; @Autowired private TokenStore tokenStore; @Override public void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/user/login","/user/register").permitAll() .antMatchers("/**").authenticated(); } @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.tokenStore(tokenStore).resourceId(SERVER_RESOURCE_ID); } }
@RestController @Api("系统用户接口") @RequestMapping(value = "/user",produces = MediaType.APPLICATION_JSON_UTF8_VALUE) @Slf4j public class SysUserController { @Autowired private SysUserLoginService userService; @Autowired private LoginUserService loginUserService;
@ApiOperation("注册方法") @PostMapping(value = "/register") public BaseRetBean register(@RequestParam(value = "username",required = true)String username, @RequestParam(value = "password",required = true)String password){ BaseRetBean baseRetBean = new BaseRetBean(1,"注册成功"); if(StrUtil.isBlank(username) || StrUtil.isBlank(password)){ baseRetBean.setRet(0); baseRetBean.setMsg("用户或密码不能为空!"); return baseRetBean; } try { boolean bool = loginUserService.register(username,password); if(!bool){ baseRetBean.setRet(0); baseRetBean.setMsg("用户名已存在"); } }catch (Exception e){ baseRetBean.setRet(-1); baseRetBean.setMsg("注册失败"); log.debug("用户:{} 注册失败,失败原因:{}",username,e); } return baseRetBean; }
@ApiOperation("登录方法") @PostMapping(value = "/login") public BaseRetBean login(@RequestParam(value = "username",required = true)String username, @RequestParam(value = "password",required = true)String password){ BaseRetBean baseRetBean = new BaseRetBean(1,"登录成功"); if(StrUtil.isBlank(username) || StrUtil.isBlank(password)){ baseRetBean.setRet(0); baseRetBean.setMsg("用户或密码不能为空!"); return baseRetBean; } try { UserLoginBean userLogin = loginUserService.login(username,password); if(userLogin == null){ baseRetBean.setRet(0); baseRetBean.setMsg("登录失败"); } baseRetBean.setData(userLogin); }catch (Exception e){ baseRetBean.setRet(-1); baseRetBean.setMsg("登录失败"); log.debug("用户:{} 登录失败,失败原因:{}",username,e); } return baseRetBean; } }
@Service @Slf4j public class LoginUserService { @Autowired private Sys_userMapper userMapper; @Autowired private SysConfig sysConfig; @Autowired private UaaFeignService uaaFeignService; @Autowired private RedisUtil redisUtil; /** * 登录方法 * @param username * @param password * @return */ public UserLoginBean login(String username, String password){ Sys_user user = userMapper.queryUserByName(username); if(user == null){ log.debug("用户:{} 登录查询失败",username); return null; } if(!BPwdUtil.matches(password,user.getPassword())){ log.debug("用户:{} 登录密码错误",username); } JWT jwt = uaaFeignService.getToken( sysConfig.getClientid(),sysConfig.getClientsecret(), "password",username,password); if(jwt == null){ log.debug("用户:{} token 签发错误",username); return null; } //api用户bean UserLoginBean loginBean = new UserLoginBean(); loginBean.setJwt(jwt); user.setPassword(null); loginBean.setUser(user); String token = StrUtil.uuid().replace("-",""); redisUtil.set(sysConfig.getTokenkeyPrefex()+"::"+token,loginBean,24*60*60); return loginBean; } public boolean register(String username,String password){ Sys_user user = userMapper.queryUserByName(username); //用户已存在 if(user != null){ return false; } Sys_user adduser = new Sys_user(); BeanUtils.copyProperties(new HandleBean().post(),adduser); adduser.setUsername(username); adduser.setPassword(new BCryptPasswordEncoder().encode(password)); userMapper.insertSelective(adduser); return true; } }
登录用到的feigncilent 和配置SysConfig
@FeignClient(value = "inc-uaa",fallback = UaaFeignServiceFullback.class) public interface UaaFeignService { @PostMapping("/oauth/token") JWT getToken(@RequestParam(value = "client_id",required = true)String cilentid, @RequestParam(value = "client_secret",required = true)String clientsecret, @RequestParam(value = "grant_type",required = true)String type, @RequestParam(value = "username",required = true)String username, @RequestParam(value = "password",required = true)String password); } @Setter @Getter @Component @ConfigurationProperties(prefix = "sys") public class SysConfig { private String tokenExcepPattern; private String tokenkeyPrefex; private String token; private String clientid; private String clientsecret; } @Getter @Setter public class JWT { private String access_token; private String token_type; private String refresh_token; private int expires_in; private String scope; private String jti; }
本文来自博客园,作者:洛神灬殇,转载请注明原文链接:https://www.cnblogs.com/liboware/p/12510458.html,任何足够先进的科技,都与魔法无异。