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); } // public static void main(String[] args) { // System.out.println(new BCryptPasswordEncoder().encode("123456")); // } }

认证服务器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;
}
 

猜你喜欢

转载自www.cnblogs.com/liboware/p/12510458.html