• 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;
    }
     
  • 相关阅读:
    【转载】分析商品日均销量(DMS)对促销商品选择的意义
    日志备份和差异备份还原中的常见问题示例(转自&邹建)
    SQL Server 2000中的完整备份、差异备份操作
    数据库差异备份与增量备份的不同之处
    差异备份和还原操作方法(转)
    SQL备份(全)
    Microsoft SQL2000 错误代码 (@@error)
    图解SQL的inner join(join)、left join、right join、full outer join、union、union all的区别
    arm-none-linux-gnueabi-gcc command not found
    关于ST-Link的internal command error问题的解决方法
  • 原文地址:https://www.cnblogs.com/liboware/p/12510458.html
Copyright © 2020-2023  润新知